summaryrefslogtreecommitdiff
path: root/chromium/headless
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2018-01-29 16:35:13 +0100
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2018-02-01 15:33:35 +0000
commitc8c2d1901aec01e934adf561a9fdf0cc776cdef8 (patch)
tree9157c3d9815e5870799e070b113813bec53e0535 /chromium/headless
parentabefd5095b41dac94ca451d784ab6e27372e981a (diff)
downloadqtwebengine-chromium-c8c2d1901aec01e934adf561a9fdf0cc776cdef8.tar.gz
BASELINE: Update Chromium to 64.0.3282.139
Change-Id: I1cae68fe9c94ff7608b26b8382fc19862cdb293a Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'chromium/headless')
-rw-r--r--chromium/headless/BUILD.gn29
-rw-r--r--chromium/headless/DEPS3
-rw-r--r--chromium/headless/app/headless_example.cc5
-rw-r--r--chromium/headless/app/headless_shell.cc58
-rw-r--r--chromium/headless/app/headless_shell.h4
-rw-r--r--chromium/headless/app/headless_shell_switches.cc10
-rw-r--r--chromium/headless/app/headless_shell_switches.h1
-rw-r--r--chromium/headless/app/shell_navigation_request.cc2
-rw-r--r--chromium/headless/lib/browser/devtools_api/domain_cc.template4
-rw-r--r--chromium/headless/lib/browser/devtools_api/domain_type_conversions_h.template5
-rw-r--r--chromium/headless/lib/browser/devtools_api/domain_types_cc.template7
-rw-r--r--chromium/headless/lib/browser/headless_browser_context_impl.cc19
-rw-r--r--chromium/headless/lib/browser/headless_browser_context_impl.h3
-rw-r--r--chromium/headless/lib/browser/headless_browser_context_options.cc4
-rw-r--r--chromium/headless/lib/browser/headless_browser_context_options.h3
-rw-r--r--chromium/headless/lib/browser/headless_browser_impl.cc24
-rw-r--r--chromium/headless/lib/browser/headless_browser_impl.h10
-rw-r--r--chromium/headless/lib/browser/headless_browser_main_parts.cc2
-rw-r--r--chromium/headless/lib/browser/headless_clipboard.cc4
-rw-r--r--chromium/headless/lib/browser/headless_content_browser_client.cc68
-rw-r--r--chromium/headless/lib/browser/headless_content_browser_client.h20
-rw-r--r--chromium/headless/lib/browser/headless_devtools_client_impl.cc36
-rw-r--r--chromium/headless/lib/browser/headless_devtools_manager_delegate.cc272
-rw-r--r--chromium/headless/lib/browser/headless_devtools_manager_delegate.h6
-rw-r--r--chromium/headless/lib/browser/headless_focus_client.cc2
-rw-r--r--chromium/headless/lib/browser/headless_network_conditions.cc2
-rw-r--r--chromium/headless/lib/browser/headless_network_delegate.cc6
-rw-r--r--chromium/headless/lib/browser/headless_network_delegate.h2
-rw-r--r--chromium/headless/lib/browser/headless_permission_manager.cc16
-rw-r--r--chromium/headless/lib/browser/headless_permission_manager.h8
-rw-r--r--chromium/headless/lib/browser/headless_platform_event_source.cc4
-rw-r--r--chromium/headless/lib/browser/headless_print_manager.cc2
-rw-r--r--chromium/headless/lib/browser/headless_quota_permission_context.cc4
-rw-r--r--chromium/headless/lib/browser/headless_resource_dispatcher_host_delegate.cc12
-rw-r--r--chromium/headless/lib/browser/headless_resource_dispatcher_host_delegate.h7
-rw-r--r--chromium/headless/lib/browser/headless_screen.cc2
-rw-r--r--chromium/headless/lib/browser/headless_tab_socket_impl.cc4
-rw-r--r--chromium/headless/lib/browser/headless_url_request_context_getter.cc58
-rw-r--r--chromium/headless/lib/browser/headless_url_request_context_getter.h1
-rw-r--r--chromium/headless/lib/browser/headless_web_contents_impl.cc20
-rw-r--r--chromium/headless/lib/browser/headless_web_contents_impl.h6
-rw-r--r--chromium/headless/lib/dom_tree_extraction_expected_nodes.txt18
-rw-r--r--chromium/headless/lib/frame_id_browsertest.cc61
-rw-r--r--chromium/headless/lib/headless_browser_browsertest.cc125
-rw-r--r--chromium/headless/lib/headless_browser_context_browsertest.cc38
-rw-r--r--chromium/headless/lib/headless_content_client.cc2
-rw-r--r--chromium/headless/lib/headless_content_main_delegate.cc13
-rw-r--r--chromium/headless/lib/headless_content_main_delegate.h2
-rw-r--r--chromium/headless/lib/headless_devtools_client_browsertest.cc193
-rw-r--r--chromium/headless/lib/headless_web_contents_browsertest.cc270
-rw-r--r--chromium/headless/lib/renderer/headless_content_renderer_client.cc4
-rw-r--r--chromium/headless/lib/renderer/headless_print_render_frame_helper_delegate.cc10
-rw-r--r--chromium/headless/lib/renderer/headless_print_render_frame_helper_delegate.h6
-rw-r--r--chromium/headless/lib/renderer/headless_render_frame_controller_impl.cc3
-rw-r--r--chromium/headless/lib/renderer/headless_tab_socket_bindings.cc2
-rw-r--r--chromium/headless/lib/utility/headless_content_utility_client.cc2
-rw-r--r--chromium/headless/lib/virtual_time_browsertest.cc501
-rw-r--r--chromium/headless/public/headless_browser.cc26
-rw-r--r--chromium/headless/public/headless_browser.h46
-rw-r--r--chromium/headless/public/headless_browser_context.h4
-rw-r--r--chromium/headless/public/headless_devtools_client.h8
-rw-r--r--chromium/headless/public/internal/headless_devtools_client_impl.h8
-rw-r--r--chromium/headless/public/internal/value_conversions.h33
-rw-r--r--chromium/headless/public/util/DEPS6
-rw-r--r--chromium/headless/public/util/black_hole_protocol_handler.cc6
-rw-r--r--chromium/headless/public/util/compositor_controller.cc429
-rw-r--r--chromium/headless/public/util/compositor_controller.h138
-rw-r--r--chromium/headless/public/util/compositor_controller_browsertest.cc110
-rw-r--r--chromium/headless/public/util/compositor_controller_unittest.cc511
-rw-r--r--chromium/headless/public/util/deterministic_dispatcher.cc4
-rw-r--r--chromium/headless/public/util/deterministic_dispatcher_test.cc6
-rw-r--r--chromium/headless/public/util/deterministic_http_protocol_handler.cc4
-rw-r--r--chromium/headless/public/util/error_reporter.cc9
-rw-r--r--chromium/headless/public/util/error_reporter.h18
-rw-r--r--chromium/headless/public/util/expedited_dispatcher.cc2
-rw-r--r--chromium/headless/public/util/expedited_dispatcher_test.cc6
-rw-r--r--chromium/headless/public/util/generic_url_request_job.cc150
-rw-r--r--chromium/headless/public/util/generic_url_request_job.h8
-rw-r--r--chromium/headless/public/util/generic_url_request_job_test.cc158
-rw-r--r--chromium/headless/public/util/http_url_fetcher.cc4
-rw-r--r--chromium/headless/public/util/in_memory_protocol_handler.cc4
-rw-r--r--chromium/headless/public/util/in_memory_request_job.cc2
-rw-r--r--chromium/headless/public/util/moveable_auto_lock.h64
-rw-r--r--chromium/headless/public/util/testing/generic_url_request_mocks.cc29
-rw-r--r--chromium/headless/public/util/testing/generic_url_request_mocks.h16
-rw-r--r--chromium/headless/public/util/testing/mock_devtools_agent_host.cc4
-rw-r--r--chromium/headless/public/util/testing/test_in_memory_protocol_handler.cc34
-rw-r--r--chromium/headless/public/util/testing/test_in_memory_protocol_handler.h19
-rw-r--r--chromium/headless/public/util/throttled_dispatcher.cc2
-rw-r--r--chromium/headless/public/util/throttled_dispatcher_test.cc4
-rw-r--r--chromium/headless/public/util/virtual_time_controller.cc107
-rw-r--r--chromium/headless/public/util/virtual_time_controller.h66
-rw-r--r--chromium/headless/public/util/virtual_time_controller_test.cc265
93 files changed, 3481 insertions, 834 deletions
diff --git a/chromium/headless/BUILD.gn b/chromium/headless/BUILD.gn
index e93546c4b99..912c913d763 100644
--- a/chromium/headless/BUILD.gn
+++ b/chromium/headless/BUILD.gn
@@ -39,6 +39,7 @@ repack("pak") {
"$root_gen_dir/content/browser/tracing/tracing_resources.pak",
"$root_gen_dir/content/content_resources.pak",
"$root_gen_dir/headless/headless_lib_resources.pak",
+ "$root_gen_dir/mojo/public/js/mojo_bindings_resources.pak",
"$root_gen_dir/net/net_resources.pak",
"$root_gen_dir/ui/resources/ui_resources_100_percent.pak",
"$root_gen_dir/ui/resources/webui_resources.pak",
@@ -61,6 +62,7 @@ repack("pak") {
"//content/app/strings",
"//content/browser/devtools:resources",
"//content/browser/tracing:resources",
+ "//mojo/public/js:resources",
"//net:net_resources",
"//third_party/WebKit/public:resources",
"//third_party/WebKit/public:scaled_resources_100_percent",
@@ -172,6 +174,7 @@ devtools_domains = [
"memory",
"network",
"page",
+ "performance",
"profiler",
"runtime",
"security",
@@ -308,6 +311,8 @@ component("headless") {
"public/internal/value_conversions.h",
"public/util/black_hole_protocol_handler.cc",
"public/util/black_hole_protocol_handler.h",
+ "public/util/compositor_controller.cc",
+ "public/util/compositor_controller.h",
"public/util/deterministic_dispatcher.cc",
"public/util/deterministic_dispatcher.h",
"public/util/deterministic_http_protocol_handler.cc",
@@ -385,6 +390,7 @@ component("headless") {
":headless_render_frame_controller",
":tab_socket",
":version_header",
+ "//components/cookie_config",
"//components/security_state/core",
"//content/public/common",
"//content/public/common:service_names",
@@ -396,6 +402,10 @@ component("headless") {
"//url",
]
+ if (is_linux && !is_chromeos) {
+ deps += [ "//components/os_crypt" ]
+ }
+
if (is_component_build) {
sources += [
"lib/browser/headless_content_browser_client.cc",
@@ -415,6 +425,7 @@ component("headless") {
]
deps += [
+ "//components/crash/core/common:crash_key",
"//components/security_state/content",
"//gin",
"//third_party/WebKit/public:blink",
@@ -485,6 +496,7 @@ if (!is_component_build) {
deps = [
":headless",
+ "//components/crash/core/common:crash_key",
"//third_party/WebKit/public:blink_headers",
"//ui/base",
"//v8",
@@ -541,6 +553,7 @@ group("headless_tests") {
test("headless_unittests") {
sources = [
"public/domains/types_unittest.cc",
+ "public/util/compositor_controller_unittest.cc",
"public/util/deterministic_dispatcher_test.cc",
"public/util/error_reporter_unittest.cc",
"public/util/expedited_dispatcher_test.cc",
@@ -651,7 +664,7 @@ if (is_linux) {
]
grit_flags = [
"-E",
- "gen_root=" + rebase_path(root_gen_dir),
+ "gen_root=" + rebase_path(root_gen_dir, root_build_dir),
]
deps = [
":js_devtools_bindings_test",
@@ -678,6 +691,7 @@ test("headless_browsertests") {
"lib/headless_devtools_client_browsertest.cc",
"lib/headless_web_contents_browsertest.cc",
"lib/virtual_time_browsertest.cc",
+ "public/util/compositor_controller_browsertest.cc",
"public/util/testing/fake_managed_dispatch_url_request_job.cc",
"public/util/testing/fake_managed_dispatch_url_request_job.h",
"public/util/testing/generic_url_request_mocks.cc",
@@ -686,6 +700,9 @@ test("headless_browsertests") {
"public/util/testing/test_in_memory_protocol_handler.h",
"test/headless_browser_test.cc",
"test/headless_browser_test.h",
+ "test/headless_render_browsertest.cc",
+ "test/headless_render_test.cc",
+ "test/headless_render_test.h",
"test/headless_test_launcher.cc",
"test/tab_socket_test.cc",
"test/tab_socket_test.h",
@@ -789,7 +806,10 @@ if (is_win) {
"lib/headless_content_main_delegate.h",
"lib/headless_content_main_delegate_win.cc",
]
- deps += [ "//third_party/WebKit/public:blink_headers" ]
+ deps += [
+ "//components/crash/core/common:crash_key",
+ "//third_party/WebKit/public:blink_headers",
+ ]
}
configs += [ ":headless_implementation" ]
@@ -826,7 +846,10 @@ if (is_win) {
"lib/utility/headless_content_utility_client.cc",
"lib/utility/headless_content_utility_client.h",
]
- deps += [ "//third_party/WebKit/public:blink_headers" ]
+ deps += [
+ "//components/crash/core/common:crash_key",
+ "//third_party/WebKit/public:blink_headers",
+ ]
}
configs += [ ":headless_implementation" ]
diff --git a/chromium/headless/DEPS b/chromium/headless/DEPS
index 343e456ad58..5a088fd8c27 100644
--- a/chromium/headless/DEPS
+++ b/chromium/headless/DEPS
@@ -1,6 +1,9 @@
include_rules = [
+ "+components/cookie_config",
"+components/crash/content/app",
"+components/crash/content/browser",
+ "+components/crash/core/common/crash_key.h",
+ "+components/os_crypt",
"+components/printing/service/public/cpp",
"+components/printing/service/public/interfaces",
"+content/public/app",
diff --git a/chromium/headless/app/headless_example.cc b/chromium/headless/app/headless_example.cc
index 693095a151f..8ed88a44071 100644
--- a/chromium/headless/app/headless_example.cc
+++ b/chromium/headless/app/headless_example.cc
@@ -107,13 +107,12 @@ void HeadlessExample::OnLoadEventFired(
void HeadlessExample::OnDomFetched(
std::unique_ptr<headless::runtime::EvaluateResult> result) {
- std::string dom;
// Make sure the evaluation succeeded before reading the result.
if (result->HasExceptionDetails()) {
LOG(ERROR) << "Failed to serialize document: "
<< result->GetExceptionDetails()->GetText();
- } else if (result->GetResult()->GetValue()->GetAsString(&dom)) {
- printf("%s\n", dom.c_str());
+ } else {
+ printf("%s\n", result->GetResult()->GetValue()->GetString().c_str());
}
// Shut down the browser (see ~HeadlessExample).
diff --git a/chromium/headless/app/headless_shell.cc b/chromium/headless/app/headless_shell.cc
index 215bd7005a9..1c6537d152c 100644
--- a/chromium/headless/app/headless_shell.cc
+++ b/chromium/headless/app/headless_shell.cc
@@ -61,7 +61,8 @@ const char kDefaultScreenshotFileName[] = "screenshot.png";
// Default file name for pdf. Can be overriden by "--print-to-pdf" switch.
const char kDefaultPDFFileName[] = "output.pdf";
-bool ParseWindowSize(std::string window_size, gfx::Size* parsed_window_size) {
+bool ParseWindowSize(const std::string& window_size,
+ gfx::Size* parsed_window_size) {
int width, height = 0;
if (sscanf(window_size.c_str(), "%d%*[x,]%d", &width, &height) >= 2 &&
width >= 0 && height >= 0) {
@@ -128,7 +129,7 @@ HeadlessShell::HeadlessShell()
weak_factory_(this) {
}
-HeadlessShell::~HeadlessShell() {}
+HeadlessShell::~HeadlessShell() = default;
#if !defined(CHROME_MULTIPLE_DLL_CHILD)
void HeadlessShell::OnStart(HeadlessBrowser* browser) {
@@ -154,8 +155,8 @@ void HeadlessShell::OnStart(HeadlessBrowser* browser) {
DeterministicHttpProtocolHandler* https_handler = nullptr;
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDeterministicFetch)) {
- deterministic_dispatcher_.reset(
- new DeterministicDispatcher(browser_->BrowserIOThread()));
+ deterministic_dispatcher_ =
+ std::make_unique<DeterministicDispatcher>(browser_->BrowserIOThread());
ProtocolHandlerMap protocol_handlers;
protocol_handlers[url::kHttpScheme] =
@@ -249,12 +250,14 @@ void HeadlessShell::DevToolsTargetReady() {
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDeterministicFetch)) {
devtools_client_->GetNetwork()->GetExperimental()->AddObserver(this);
- devtools_client_->GetNetwork()
- ->GetExperimental()
- ->SetRequestInterceptionEnabled(
- network::SetRequestInterceptionEnabledParams::Builder()
- .SetEnabled(true)
- .Build());
+ std::unique_ptr<headless::network::RequestPattern> match_all =
+ headless::network::RequestPattern::Builder().SetUrlPattern("*").Build();
+ std::vector<std::unique_ptr<headless::network::RequestPattern>> patterns;
+ patterns.push_back(std::move(match_all));
+ devtools_client_->GetNetwork()->GetExperimental()->SetRequestInterception(
+ network::SetRequestInterceptionParams::Builder()
+ .SetPatterns(std::move(patterns))
+ .Build());
}
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDefaultBackgroundColor)) {
@@ -339,9 +342,8 @@ void HeadlessShell::PollReadyState() {
void HeadlessShell::OnReadyState(
std::unique_ptr<runtime::EvaluateResult> result) {
- std::string ready_state_and_url;
- if (result->GetResult()->GetValue()->GetAsString(&ready_state_and_url)) {
- std::stringstream stream(ready_state_and_url);
+ if (result->GetResult()->GetValue()->is_string()) {
+ std::stringstream stream(result->GetResult()->GetValue()->GetString());
std::string ready_state;
std::string url;
stream >> ready_state;
@@ -424,10 +426,7 @@ void HeadlessShell::OnDomFetched(
LOG(ERROR) << "Failed to serialize document: "
<< result->GetExceptionDetails()->GetText();
} else {
- std::string dom;
- if (result->GetResult()->GetValue()->GetAsString(&dom)) {
- printf("%s\n", dom.c_str());
- }
+ printf("%s\n", result->GetResult()->GetValue()->GetString().c_str());
}
Shutdown();
}
@@ -506,23 +505,31 @@ void HeadlessShell::WriteFile(const std::string& file_path_switch,
const std::string& default_file_name,
const std::string& base64_data) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
base::FilePath file_name =
base::CommandLine::ForCurrentProcess()->GetSwitchValuePath(
file_path_switch);
if (file_name.empty())
file_name = base::FilePath().AppendASCII(default_file_name);
+ std::string decoded_data;
+ if (!base::Base64Decode(base64_data, &decoded_data)) {
+ LOG(ERROR) << "Failed to decode base64 data";
+ OnFileOpened(std::string(), file_name, base::File::FILE_ERROR_FAILED);
+ return;
+ }
+
file_proxy_ = base::MakeUnique<base::FileProxy>(file_task_runner_.get());
if (!file_proxy_->CreateOrOpen(
file_name, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE,
base::Bind(&HeadlessShell::OnFileOpened, weak_factory_.GetWeakPtr(),
- base64_data, file_name))) {
+ decoded_data, file_name))) {
// Operation could not be started.
OnFileOpened(std::string(), file_name, base::File::FILE_ERROR_FAILED);
}
}
-void HeadlessShell::OnFileOpened(const std::string& base64_data,
+void HeadlessShell::OnFileOpened(const std::string& decoded_data,
const base::FilePath file_name,
base::File::Error error_code) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
@@ -533,16 +540,7 @@ void HeadlessShell::OnFileOpened(const std::string& base64_data,
return;
}
- std::string decoded_data;
- if (!base::Base64Decode(base64_data, &decoded_data)) {
- LOG(ERROR) << "Failed to decode base64 data";
- OnFileWritten(file_name, base64_data.size(), base::File::FILE_ERROR_FAILED,
- 0);
- return;
- }
-
- scoped_refptr<net::IOBufferWithSize> buf =
- new net::IOBufferWithSize(decoded_data.size());
+ auto buf = base::MakeRefCounted<net::IOBufferWithSize>(decoded_data.size());
memcpy(buf->data(), decoded_data.data(), decoded_data.size());
if (!file_proxy_->Write(
@@ -734,7 +732,7 @@ int HeadlessShellMain(int argc, const char** argv) {
if (command_line.HasSwitch(switches::kProxyServer)) {
std::string proxy_server =
command_line.GetSwitchValueASCII(switches::kProxyServer);
- std::unique_ptr<net::ProxyConfig> proxy_config(new net::ProxyConfig);
+ auto proxy_config = std::make_unique<net::ProxyConfig>();
proxy_config->proxy_rules().ParseFromString(proxy_server);
if (command_line.HasSwitch(switches::kProxyBypassList)) {
std::string bypass_list =
diff --git a/chromium/headless/app/headless_shell.h b/chromium/headless/app/headless_shell.h
index 58803aec48e..1b33c333824 100644
--- a/chromium/headless/app/headless_shell.h
+++ b/chromium/headless/app/headless_shell.h
@@ -90,8 +90,8 @@ class HeadlessShell : public HeadlessWebContents::Observer,
void WriteFile(const std::string& switch_string,
const std::string& default_file_name,
- const std::string& data);
- void OnFileOpened(const std::string& data,
+ const std::string& base64_data);
+ void OnFileOpened(const std::string& decoded_data,
const base::FilePath file_name,
base::File::Error error_code);
void OnFileWritten(const base::FilePath file_name,
diff --git a/chromium/headless/app/headless_shell_switches.cc b/chromium/headless/app/headless_shell_switches.cc
index c98f7954e5b..e2cabfa3bcf 100644
--- a/chromium/headless/app/headless_shell_switches.cc
+++ b/chromium/headless/app/headless_shell_switches.cc
@@ -33,6 +33,16 @@ const char kDumpDom[] = "dump-dom";
// Hide scrollbars from screenshots.
const char kHideScrollbars[] = "hide-scrollbars";
+// Specifies which encryption storage backend to use. Possible values are
+// kwallet, kwallet5, gnome, gnome-keyring, gnome-libsecret, basic. Any other
+// value will lead to Chrome detecting the best backend automatically.
+// TODO(crbug.com/571003): Once PasswordStore no longer uses the Keyring or
+// KWallet for storing passwords, rename this flag to stop referencing
+// passwords. Do not rename it sooner, though; developers and testers might
+// rely on it keeping large amounts of testing passwords out of their Keyrings
+// or KWallets.
+const char kPasswordStore[] = "password-store";
+
// Save a pdf file of the loaded page.
const char kPrintToPDF[] = "print-to-pdf";
diff --git a/chromium/headless/app/headless_shell_switches.h b/chromium/headless/app/headless_shell_switches.h
index 2136c7cfea3..6011f3c22d0 100644
--- a/chromium/headless/app/headless_shell_switches.h
+++ b/chromium/headless/app/headless_shell_switches.h
@@ -17,6 +17,7 @@ extern const char kDisableCrashReporter[];
extern const char kDumpDom[];
extern const char kEnableCrashReporter[];
extern const char kHideScrollbars[];
+extern const char kPasswordStore[];
extern const char kPrintToPDF[];
extern const char kProxyBypassList[];
extern const char kProxyServer[];
diff --git a/chromium/headless/app/shell_navigation_request.cc b/chromium/headless/app/shell_navigation_request.cc
index dc5d7c702bd..3f5344d7d02 100644
--- a/chromium/headless/app/shell_navigation_request.cc
+++ b/chromium/headless/app/shell_navigation_request.cc
@@ -18,7 +18,7 @@ ShellNavigationRequest::ShellNavigationRequest(
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
}
-ShellNavigationRequest::~ShellNavigationRequest() {}
+ShellNavigationRequest::~ShellNavigationRequest() = default;
void ShellNavigationRequest::StartProcessing(base::Closure done_callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
diff --git a/chromium/headless/lib/browser/devtools_api/domain_cc.template b/chromium/headless/lib/browser/devtools_api/domain_cc.template
index de9425c33c8..ac16d62dd52 100644
--- a/chromium/headless/lib/browser/devtools_api/domain_cc.template
+++ b/chromium/headless/lib/browser/devtools_api/domain_cc.template
@@ -110,7 +110,7 @@ void Domain::Handle{{method_name}}Response(base::Callback<void(std::unique_ptr<{
}
ErrorReporter errors;
std::unique_ptr<{{method_name}}Result> result = {{method_name}}Result::Parse(response, &errors);
- DCHECK(!errors.HasErrors());
+ DCHECK(!errors.HasErrors()) << errors.ToString();
callback.Run(std::move(result));
}
{% endfor %}
@@ -121,7 +121,7 @@ void Domain::Handle{{method_name}}Response(base::Callback<void(std::unique_ptr<{
void Domain::Dispatch{{event.name | to_title_case}}Event(const base::Value& params) {
ErrorReporter errors;
std::unique_ptr<{{event.name | to_title_case}}Params> parsed_params({{event.name | to_title_case}}Params::Parse(params, &errors));
- DCHECK(!errors.HasErrors());
+ DCHECK(!errors.HasErrors()) << errors.ToString();
for (ExperimentalObserver& observer : observers_) {
observer.On{{event.name | to_title_case}}(*parsed_params);
}
diff --git a/chromium/headless/lib/browser/devtools_api/domain_type_conversions_h.template b/chromium/headless/lib/browser/devtools_api/domain_type_conversions_h.template
index 15796eb756d..eb8297050ce 100644
--- a/chromium/headless/lib/browser/devtools_api/domain_type_conversions_h.template
+++ b/chromium/headless/lib/browser/devtools_api/domain_type_conversions_h.template
@@ -20,14 +20,13 @@ template <>
struct FromValue<{{namespace}}::{{type.id}}> {
static {{namespace}}::{{type.id}} Parse(const base::Value& value, ErrorReporter* errors) {
{% set default = namespace + '::' + type.id + '::' + type.enum[0] | sanitize_literal | dash_to_camelcase | camelcase_to_hacker_style | upper %}
- std::string string_value;
- if (!value.GetAsString(&string_value)) {
+ if (!value.is_string()) {
errors->AddError("string enum value expected");
{# Return an arbitrary enum member -- the caller will just ignore it. #}
return {{default}};
}
{% for literal in type.enum %}
- if (string_value == "{{literal}}")
+ if (value.GetString() == "{{literal}}")
return {{namespace}}::{{type.id}}::{{literal | sanitize_literal | dash_to_camelcase | camelcase_to_hacker_style | upper }};
{% endfor %}
errors->AddError("invalid enum value");
diff --git a/chromium/headless/lib/browser/devtools_api/domain_types_cc.template b/chromium/headless/lib/browser/devtools_api/domain_types_cc.template
index bd1c2773dd4..be125aeb835 100644
--- a/chromium/headless/lib/browser/devtools_api/domain_types_cc.template
+++ b/chromium/headless/lib/browser/devtools_api/domain_types_cc.template
@@ -31,8 +31,7 @@ namespace {{domain.domain | camelcase_to_hacker_style}} {
std::unique_ptr<{{type.id}}> {{type.id}}::Parse(const base::Value& value, ErrorReporter* errors) {
errors->Push();
errors->SetName("{{type.id}}");
- const base::DictionaryValue* object;
- if (!value.GetAsDictionary(&object)) {
+ if (!value.is_dict()) {
errors->AddError("object expected");
errors->Pop();
return nullptr;
@@ -43,8 +42,8 @@ std::unique_ptr<{{type.id}}> {{type.id}}::Parse(const base::Value& value, ErrorR
errors->SetName("{{type.id}}");
{% for property in type.properties %}
{% set value_name = property.name | camelcase_to_hacker_style + "_value" %}
- const base::Value* {{value_name}};
- if (object->Get("{{property.name}}", &{{value_name}})) {
+ const base::Value* {{value_name}} = value.FindKey("{{property.name}}");
+ if ({{value_name}}) {
errors->SetName("{{property.name}}");
{% if property.optional %}
result->{{property.name | camelcase_to_hacker_style}}_ = internal::FromValue<{{resolve_type(property).raw_type}}>::Parse(*{{value_name}}, errors);
diff --git a/chromium/headless/lib/browser/headless_browser_context_impl.cc b/chromium/headless/lib/browser/headless_browser_context_impl.cc
index 781c0abe9e3..17849e27819 100644
--- a/chromium/headless/lib/browser/headless_browser_context_impl.cc
+++ b/chromium/headless/lib/browser/headless_browser_context_impl.cc
@@ -83,9 +83,9 @@ HeadlessBrowserContextImpl::HeadlessBrowserContextImpl(
std::unique_ptr<HeadlessBrowserContextOptions> context_options)
: browser_(browser),
context_options_(std::move(context_options)),
- resource_context_(new HeadlessResourceContext),
+ resource_context_(std::make_unique<HeadlessResourceContext>()),
should_remove_headers_(true),
- permission_manager_(new HeadlessPermissionManager()),
+ permission_manager_(std::make_unique<HeadlessPermissionManager>(this)),
id_(base::GenerateGUID()) {
InitWhileIOAllowed();
}
@@ -201,7 +201,8 @@ void HeadlessBrowserContextImpl::Close() {
void HeadlessBrowserContextImpl::InitWhileIOAllowed() {
if (!context_options_->user_data_dir().empty()) {
- path_ = context_options_->user_data_dir();
+ path_ =
+ context_options_->user_data_dir().Append(FILE_PATH_LITERAL("Default"));
} else {
PathService::Get(base::DIR_EXE, &path_);
}
@@ -253,8 +254,6 @@ HeadlessBrowserContextImpl::GetSSLHostStateDelegate() {
}
content::PermissionManager* HeadlessBrowserContextImpl::GetPermissionManager() {
- if (!permission_manager_.get())
- permission_manager_.reset(new HeadlessPermissionManager());
return permission_manager_.get();
}
@@ -476,6 +475,12 @@ HeadlessBrowserContext::Builder::SetIncognitoMode(bool incognito_mode) {
}
HeadlessBrowserContext::Builder&
+HeadlessBrowserContext::Builder::SetAllowCookies(bool allow_cookies) {
+ options_->allow_cookies_ = allow_cookies;
+ return *this;
+}
+
+HeadlessBrowserContext::Builder&
HeadlessBrowserContext::Builder::AddTabSocketMojoBindings() {
std::string js_bindings =
ui::ResourceBundle::GetSharedInstance()
@@ -514,13 +519,13 @@ HeadlessBrowserContext* HeadlessBrowserContext::Builder::Build() {
return browser_->CreateBrowserContext(this);
}
-HeadlessBrowserContext::Builder::MojoBindings::MojoBindings() {}
+HeadlessBrowserContext::Builder::MojoBindings::MojoBindings() = default;
HeadlessBrowserContext::Builder::MojoBindings::MojoBindings(
const std::string& mojom_name,
const std::string& js_bindings)
: mojom_name(mojom_name), js_bindings(js_bindings) {}
-HeadlessBrowserContext::Builder::MojoBindings::~MojoBindings() {}
+HeadlessBrowserContext::Builder::MojoBindings::~MojoBindings() = default;
} // namespace headless
diff --git a/chromium/headless/lib/browser/headless_browser_context_impl.h b/chromium/headless/lib/browser/headless_browser_context_impl.h
index 580b8b0e15f..d84727c1a5d 100644
--- a/chromium/headless/lib/browser/headless_browser_context_impl.h
+++ b/chromium/headless/lib/browser/headless_browser_context_impl.h
@@ -13,7 +13,6 @@
#include "base/files/file_path.h"
#include "base/unguessable_token.h"
#include "content/public/browser/browser_context.h"
-#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/resource_context.h"
#include "headless/lib/browser/headless_browser_context_options.h"
#include "headless/lib/browser/headless_network_conditions.h"
@@ -120,7 +119,7 @@ class HeadlessBrowserContextImpl : public HeadlessBrowserContext,
bool canceled_by_devtools);
void SetNetworkConditions(HeadlessNetworkConditions conditions);
- HeadlessNetworkConditions GetNetworkConditions();
+ HeadlessNetworkConditions GetNetworkConditions() override;
private:
HeadlessBrowserContextImpl(
diff --git a/chromium/headless/lib/browser/headless_browser_context_options.cc b/chromium/headless/lib/browser/headless_browser_context_options.cc
index 5898576830a..93e078083b7 100644
--- a/chromium/headless/lib/browser/headless_browser_context_options.cc
+++ b/chromium/headless/lib/browser/headless_browser_context_options.cc
@@ -74,6 +74,10 @@ bool HeadlessBrowserContextOptions::incognito_mode() const {
browser_options_->incognito_mode);
}
+bool HeadlessBrowserContextOptions::allow_cookies() const {
+ return ReturnOverriddenValue(allow_cookies_, browser_options_->allow_cookies);
+}
+
const base::Callback<void(WebPreferences*)>&
HeadlessBrowserContextOptions::override_web_preferences_callback() const {
return ReturnOverriddenValue(
diff --git a/chromium/headless/lib/browser/headless_browser_context_options.h b/chromium/headless/lib/browser/headless_browser_context_options.h
index 23a4f6251a7..00e1295f751 100644
--- a/chromium/headless/lib/browser/headless_browser_context_options.h
+++ b/chromium/headless/lib/browser/headless_browser_context_options.h
@@ -44,6 +44,8 @@ class HeadlessBrowserContextOptions {
// Set HeadlessBrowser::Options::incognito_mode.
bool incognito_mode() const;
+ bool allow_cookies() const;
+
// Custom network protocol handlers. These can be used to override URL
// fetching for different network schemes.
const ProtocolHandlerMap& protocol_handlers() const;
@@ -70,6 +72,7 @@ class HeadlessBrowserContextOptions {
base::Optional<gfx::Size> window_size_;
base::Optional<base::FilePath> user_data_dir_;
base::Optional<bool> incognito_mode_;
+ base::Optional<bool> allow_cookies_;
base::Optional<base::Callback<void(WebPreferences*)>>
override_web_preferences_callback_;
diff --git a/chromium/headless/lib/browser/headless_browser_impl.cc b/chromium/headless/lib/browser/headless_browser_impl.cc
index bbd231c1bb3..88bede99b53 100644
--- a/chromium/headless/lib/browser/headless_browser_impl.cc
+++ b/chromium/headless/lib/browser/headless_browser_impl.cc
@@ -77,7 +77,7 @@ HeadlessBrowserImpl::HeadlessBrowserImpl(
agent_host_(nullptr),
weak_ptr_factory_(this) {}
-HeadlessBrowserImpl::~HeadlessBrowserImpl() {}
+HeadlessBrowserImpl::~HeadlessBrowserImpl() = default;
HeadlessBrowserContext::Builder
HeadlessBrowserImpl::CreateBrowserContextBuilder() {
@@ -103,7 +103,10 @@ void HeadlessBrowserImpl::Shutdown() {
weak_ptr_factory_.InvalidateWeakPtrs();
// Destroy all browser contexts.
- browser_contexts_.clear();
+ {
+ base::AutoLock lock(browser_contexts_lock_);
+ browser_contexts_.clear();
+ }
BrowserMainThread()->PostTask(FROM_HERE,
base::MessageLoop::QuitWhenIdleClosure());
@@ -112,6 +115,7 @@ void HeadlessBrowserImpl::Shutdown() {
std::vector<HeadlessBrowserContext*>
HeadlessBrowserImpl::GetAllBrowserContexts() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ base::AutoLock lock(browser_contexts_lock_);
std::vector<HeadlessBrowserContext*> result;
result.reserve(browser_contexts_.size());
@@ -162,6 +166,7 @@ HeadlessBrowserContext* HeadlessBrowserImpl::CreateBrowserContext(
HeadlessBrowserContext* result = browser_context.get();
+ base::AutoLock lock(browser_contexts_lock_);
browser_contexts_[browser_context->Id()] = std::move(browser_context);
return result;
@@ -169,6 +174,7 @@ HeadlessBrowserContext* HeadlessBrowserImpl::CreateBrowserContext(
void HeadlessBrowserImpl::DestroyBrowserContext(
HeadlessBrowserContextImpl* browser_context) {
+ base::AutoLock lock(browser_contexts_lock_);
auto it = browser_contexts_.find(browser_context->Id());
DCHECK(it != browser_contexts_.end());
browser_contexts_.erase(it);
@@ -218,12 +224,26 @@ HeadlessWebContentsImpl* HeadlessBrowserImpl::GetWebContentsForWindowId(
HeadlessBrowserContext* HeadlessBrowserImpl::GetBrowserContextForId(
const std::string& id) {
+ base::AutoLock lock(browser_contexts_lock_);
auto find_it = browser_contexts_.find(id);
if (find_it == browser_contexts_.end())
return nullptr;
return find_it->second.get();
}
+LockedPtr<HeadlessBrowserContextImpl>
+HeadlessBrowserImpl::GetBrowserContextForRenderFrame(
+ int render_process_id,
+ int render_frame_id) const {
+ MoveableAutoLock lock(browser_contexts_lock_);
+ for (const auto& pair : browser_contexts_) {
+ if (pair.second->GetDevToolsFrameToken(render_process_id, render_frame_id))
+ return LockedPtr<HeadlessBrowserContextImpl>(std::move(lock),
+ pair.second.get());
+ }
+ return LockedPtr<HeadlessBrowserContextImpl>(std::move(lock), nullptr);
+}
+
HeadlessDevToolsTarget* HeadlessBrowserImpl::GetDevToolsTarget() {
return agent_host_ ? this : nullptr;
}
diff --git a/chromium/headless/lib/browser/headless_browser_impl.h b/chromium/headless/lib/browser/headless_browser_impl.h
index 591e50a0532..7f4ec64dc20 100644
--- a/chromium/headless/lib/browser/headless_browser_impl.h
+++ b/chromium/headless/lib/browser/headless_browser_impl.h
@@ -18,6 +18,7 @@
#include "headless/lib/browser/headless_devtools_manager_delegate.h"
#include "headless/lib/browser/headless_web_contents_impl.h"
#include "headless/public/headless_export.h"
+#include "headless/public/util/moveable_auto_lock.h"
namespace ui {
class Compositor;
@@ -78,6 +79,12 @@ class HEADLESS_EXPORT HeadlessBrowserImpl : public HeadlessBrowser,
base::WeakPtr<HeadlessBrowserImpl> GetWeakPtr();
+ // Returns the corresponding HeadlessBrowserContextImpl or null if one can't
+ // be found. Can be called on any thread.
+ LockedPtr<HeadlessBrowserContextImpl> GetBrowserContextForRenderFrame(
+ int render_process_id,
+ int render_frame_id) const;
+
// All the methods that begin with Platform need to be implemented by the
// platform specific headless implementation.
// Helper for one time initialization of application
@@ -93,7 +100,8 @@ class HEADLESS_EXPORT HeadlessBrowserImpl : public HeadlessBrowser,
HeadlessBrowser::Options options_;
HeadlessBrowserMainParts* browser_main_parts_; // Not owned.
- std::unordered_map<std::string, std::unique_ptr<HeadlessBrowserContextImpl>>
+ mutable base::Lock browser_contexts_lock_; // Protects |browser_contexts_|
+ base::flat_map<std::string, std::unique_ptr<HeadlessBrowserContextImpl>>
browser_contexts_;
HeadlessBrowserContext* default_browser_context_; // Not owned.
diff --git a/chromium/headless/lib/browser/headless_browser_main_parts.cc b/chromium/headless/lib/browser/headless_browser_main_parts.cc
index 64e9018f5e3..da66fb7f75d 100644
--- a/chromium/headless/lib/browser/headless_browser_main_parts.cc
+++ b/chromium/headless/lib/browser/headless_browser_main_parts.cc
@@ -18,7 +18,7 @@ HeadlessBrowserMainParts::HeadlessBrowserMainParts(HeadlessBrowserImpl* browser)
: browser_(browser)
, devtools_http_handler_started_(false) {}
-HeadlessBrowserMainParts::~HeadlessBrowserMainParts() {}
+HeadlessBrowserMainParts::~HeadlessBrowserMainParts() = default;
void HeadlessBrowserMainParts::PreMainMessageLoopRun() {
const base::CommandLine* command_line =
diff --git a/chromium/headless/lib/browser/headless_clipboard.cc b/chromium/headless/lib/browser/headless_clipboard.cc
index dcadd4c36c1..9b9fd069746 100644
--- a/chromium/headless/lib/browser/headless_clipboard.cc
+++ b/chromium/headless/lib/browser/headless_clipboard.cc
@@ -13,7 +13,7 @@ namespace headless {
HeadlessClipboard::HeadlessClipboard()
: default_store_type_(ui::CLIPBOARD_TYPE_COPY_PASTE) {}
-HeadlessClipboard::~HeadlessClipboard() {}
+HeadlessClipboard::~HeadlessClipboard() = default;
void HeadlessClipboard::OnPreShutdown() {}
@@ -182,7 +182,7 @@ HeadlessClipboard::DataStore::DataStore() : sequence_number(0) {}
HeadlessClipboard::DataStore::DataStore(const DataStore& other) = default;
-HeadlessClipboard::DataStore::~DataStore() {}
+HeadlessClipboard::DataStore::~DataStore() = default;
void HeadlessClipboard::DataStore::Clear() {
data.clear();
diff --git a/chromium/headless/lib/browser/headless_content_browser_client.cc b/chromium/headless/lib/browser/headless_content_browser_client.cc
index d96b9886839..98434626717 100644
--- a/chromium/headless/lib/browser/headless_content_browser_client.cc
+++ b/chromium/headless/lib/browser/headless_content_browser_client.cc
@@ -14,6 +14,7 @@
#include "base/memory/ptr_util.h"
#include "base/path_service.h"
#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/resource_dispatcher_host.h"
@@ -114,9 +115,11 @@ int GetCrashSignalFD(const base::CommandLine& command_line,
HeadlessContentBrowserClient::HeadlessContentBrowserClient(
HeadlessBrowserImpl* browser)
- : browser_(browser) {}
+ : browser_(browser),
+ append_command_line_flags_callback_(
+ browser_->options()->append_command_line_flags_callback) {}
-HeadlessContentBrowserClient::~HeadlessContentBrowserClient() {}
+HeadlessContentBrowserClient::~HeadlessContentBrowserClient() = default;
content::BrowserMainParts* HeadlessContentBrowserClient::CreateBrowserMainParts(
const content::MainFunctionParams&) {
@@ -230,6 +233,9 @@ void HeadlessContentBrowserClient::GetAdditionalMappedFilesForChildProcess(
void HeadlessContentBrowserClient::AppendExtraCommandLineSwitches(
base::CommandLine* command_line,
int child_process_id) {
+ // NOTE: We may be called on the UI or IO thread. If called on the IO thread,
+ // |browser_| may have already been destroyed.
+
command_line->AppendSwitch(::switches::kHeadless);
const base::CommandLine& old_command_line(
*base::CommandLine::ForCurrentProcess());
@@ -245,8 +251,10 @@ void HeadlessContentBrowserClient::AppendExtraCommandLineSwitches(
#endif // defined(HEADLESS_USE_BREAKPAD)
// If we're spawning a renderer, then override the language switch.
- if (command_line->GetSwitchValueASCII(::switches::kProcessType) ==
- ::switches::kRendererProcess) {
+ std::string process_type =
+ command_line->GetSwitchValueASCII(::switches::kProcessType);
+ if (process_type == ::switches::kRendererProcess) {
+ // Renderer processes are initialized on the UI thread, so this is safe.
content::RenderProcessHost* render_process_host =
content::RenderProcessHost::FromID(child_process_id);
if (render_process_host) {
@@ -262,6 +270,22 @@ void HeadlessContentBrowserClient::AppendExtraCommandLineSwitches(
}
}
}
+
+ if (append_command_line_flags_callback_) {
+ HeadlessBrowserContextImpl* headless_browser_context_impl = nullptr;
+ if (process_type == ::switches::kRendererProcess) {
+ // Renderer processes are initialized on the UI thread, so this is safe.
+ content::RenderProcessHost* render_process_host =
+ content::RenderProcessHost::FromID(child_process_id);
+ if (render_process_host) {
+ headless_browser_context_impl = HeadlessBrowserContextImpl::From(
+ render_process_host->GetBrowserContext());
+ }
+ }
+ append_command_line_flags_callback_.Run(command_line,
+ headless_browser_context_impl,
+ process_type, child_process_id);
+ }
}
void HeadlessContentBrowserClient::AllowCertificateError(
@@ -290,8 +314,7 @@ void HeadlessContentBrowserClient::AllowCertificateError(
void HeadlessContentBrowserClient::ResourceDispatcherHostCreated() {
resource_dispatcher_host_delegate_.reset(
- new HeadlessResourceDispatcherHostDelegate(
- browser_->options()->enable_resource_scheduler));
+ new HeadlessResourceDispatcherHostDelegate);
content::ResourceDispatcherHost::Get()->SetDelegate(
resource_dispatcher_host_delegate_.get());
}
@@ -300,4 +323,37 @@ net::NetLog* HeadlessContentBrowserClient::GetNetLog() {
return browser_->browser_main_parts()->net_log();
}
+bool HeadlessContentBrowserClient::AllowGetCookie(
+ const GURL& url,
+ const GURL& first_party,
+ const net::CookieList& cookie_list,
+ content::ResourceContext* context,
+ int render_process_id,
+ int render_frame_id) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+ LockedPtr<HeadlessBrowserContextImpl> browser_context =
+ browser_->GetBrowserContextForRenderFrame(render_process_id,
+ render_frame_id);
+ if (!browser_context)
+ return false;
+ return browser_context->options()->allow_cookies();
+}
+
+bool HeadlessContentBrowserClient::AllowSetCookie(
+ const GURL& url,
+ const GURL& first_party,
+ const net::CanonicalCookie& cookie,
+ content::ResourceContext* context,
+ int render_process_id,
+ int render_frame_id,
+ const net::CookieOptions& options) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+ LockedPtr<HeadlessBrowserContextImpl> browser_context =
+ browser_->GetBrowserContextForRenderFrame(render_process_id,
+ render_frame_id);
+ if (!browser_context)
+ return false;
+ return browser_context->options()->allow_cookies();
+}
+
} // namespace headless
diff --git a/chromium/headless/lib/browser/headless_content_browser_client.h b/chromium/headless/lib/browser/headless_content_browser_client.h
index 929bc9f346b..6a7daae9ad8 100644
--- a/chromium/headless/lib/browser/headless_content_browser_client.h
+++ b/chromium/headless/lib/browser/headless_content_browser_client.h
@@ -7,6 +7,7 @@
#include "content/public/browser/content_browser_client.h"
#include "headless/lib/browser/headless_resource_dispatcher_host_delegate.h"
+#include "headless/public/headless_browser.h"
namespace headless {
@@ -55,6 +56,21 @@ class HeadlessContentBrowserClient : public content::ContentBrowserClient {
net::NetLog* GetNetLog() override;
+ bool AllowGetCookie(const GURL& url,
+ const GURL& first_party,
+ const net::CookieList& cookie_list,
+ content::ResourceContext* context,
+ int render_process_id,
+ int render_frame_id) override;
+
+ bool AllowSetCookie(const GURL& url,
+ const GURL& first_party,
+ const net::CanonicalCookie& cookie,
+ content::ResourceContext* context,
+ int render_process_id,
+ int render_frame_id,
+ const net::CookieOptions& options) override;
+
private:
std::unique_ptr<base::Value> GetBrowserServiceManifestOverlay();
std::unique_ptr<base::Value> GetRendererServiceManifestOverlay();
@@ -62,6 +78,10 @@ class HeadlessContentBrowserClient : public content::ContentBrowserClient {
HeadlessBrowserImpl* browser_; // Not owned.
+ // We store the callback here because we may call it from the I/O thread.
+ HeadlessBrowser::Options::AppendCommandLineFlagsCallback
+ append_command_line_flags_callback_;
+
std::unique_ptr<HeadlessResourceDispatcherHostDelegate>
resource_dispatcher_host_delegate_;
diff --git a/chromium/headless/lib/browser/headless_devtools_client_impl.cc b/chromium/headless/lib/browser/headless_devtools_client_impl.cc
index 8cf1e5e5274..9763565c59b 100644
--- a/chromium/headless/lib/browser/headless_devtools_client_impl.cc
+++ b/chromium/headless/lib/browser/headless_devtools_client_impl.cc
@@ -61,6 +61,7 @@ HeadlessDevToolsClientImpl::HeadlessDevToolsClientImpl()
memory_domain_(this),
network_domain_(this),
page_domain_(this),
+ performance_domain_(this),
profiler_domain_(this),
runtime_domain_(this),
security_domain_(this),
@@ -71,7 +72,7 @@ HeadlessDevToolsClientImpl::HeadlessDevToolsClientImpl()
content::BrowserThread::UI)),
weak_ptr_factory_(this) {}
-HeadlessDevToolsClientImpl::~HeadlessDevToolsClientImpl() {}
+HeadlessDevToolsClientImpl::~HeadlessDevToolsClientImpl() = default;
bool HeadlessDevToolsClientImpl::AttachToHost(
content::DevToolsAgentHost* agent_host) {
@@ -115,14 +116,13 @@ void HeadlessDevToolsClientImpl::SendRawDevToolsMessage(
#ifndef NDEBUG
std::unique_ptr<base::Value> message =
base::JSONReader::Read(json_message, base::JSON_PARSE_RFC);
- const base::DictionaryValue* message_dict;
- int id = 0;
- if (!message || !message->GetAsDictionary(&message_dict) ||
- !message_dict->GetInteger("id", &id)) {
- NOTREACHED() << "Badly formed message";
+ const base::Value* id_value = message->FindKey("id");
+ if (!id_value) {
+ NOTREACHED() << "Badly formed message " << json_message;
return;
}
- DCHECK_EQ((id % 2), 1) << "Raw devtools messages must have an odd ID.";
+ DCHECK_EQ((id_value->GetInt() % 2), 1)
+ << "Raw devtools messages must have an odd ID.";
#endif
agent_host_->DispatchProtocolMessage(this, json_message);
@@ -162,10 +162,10 @@ void HeadlessDevToolsClientImpl::DispatchProtocolMessage(
bool HeadlessDevToolsClientImpl::DispatchMessageReply(
const base::DictionaryValue& message_dict) {
- int id = 0;
- if (!message_dict.GetInteger("id", &id))
+ const base::Value* id_value = message_dict.FindKey("id");
+ if (!id_value)
return false;
- auto it = pending_messages_.find(id);
+ auto it = pending_messages_.find(id_value->GetInt());
if (it == pending_messages_.end()) {
NOTREACHED() << "Unexpected reply";
return false;
@@ -193,9 +193,10 @@ bool HeadlessDevToolsClientImpl::DispatchMessageReply(
bool HeadlessDevToolsClientImpl::DispatchEvent(
std::unique_ptr<base::Value> owning_message,
const base::DictionaryValue& message_dict) {
- std::string method;
- if (!message_dict.GetString("method", &method))
+ const base::Value* method_value = message_dict.FindKey("method");
+ if (!method_value)
return false;
+ const std::string& method = method_value->GetString();
if (method == "Inspector.targetCrashed")
renderer_crashed_ = true;
EventHandlerMap::const_iterator it = event_handlers_.find(method);
@@ -228,8 +229,7 @@ void HeadlessDevToolsClientImpl::DispatchEventTask(
}
void HeadlessDevToolsClientImpl::AgentHostClosed(
- content::DevToolsAgentHost* agent_host,
- bool replaced_with_another_client) {
+ content::DevToolsAgentHost* agent_host) {
DCHECK_EQ(agent_host_, agent_host);
agent_host = nullptr;
pending_messages_.clear();
@@ -340,6 +340,10 @@ page::Domain* HeadlessDevToolsClientImpl::GetPage() {
return &page_domain_;
}
+performance::Domain* HeadlessDevToolsClientImpl::GetPerformance() {
+ return &performance_domain_;
+}
+
profiler::Domain* HeadlessDevToolsClientImpl::GetProfiler() {
return &profiler_domain_;
}
@@ -412,7 +416,7 @@ void HeadlessDevToolsClientImpl::RegisterEventHandler(
event_handlers_[method] = callback;
}
-HeadlessDevToolsClientImpl::Callback::Callback() {}
+HeadlessDevToolsClientImpl::Callback::Callback() = default;
HeadlessDevToolsClientImpl::Callback::Callback(Callback&& other) = default;
@@ -423,7 +427,7 @@ HeadlessDevToolsClientImpl::Callback::Callback(
base::Callback<void(const base::Value&)> callback)
: callback_with_result(callback) {}
-HeadlessDevToolsClientImpl::Callback::~Callback() {}
+HeadlessDevToolsClientImpl::Callback::~Callback() = default;
HeadlessDevToolsClientImpl::Callback& HeadlessDevToolsClientImpl::Callback::
operator=(Callback&& other) = default;
diff --git a/chromium/headless/lib/browser/headless_devtools_manager_delegate.cc b/chromium/headless/lib/browser/headless_devtools_manager_delegate.cc
index 498aafcf51f..553d7159b51 100644
--- a/chromium/headless/lib/browser/headless_devtools_manager_delegate.cc
+++ b/chromium/headless/lib/browser/headless_devtools_manager_delegate.cc
@@ -8,6 +8,7 @@
#include <utility>
#include "base/base64.h"
+#include "base/command_line.h"
#include "base/json/json_writer.h"
#include "build/build_config.h"
#include "cc/base/switches.h"
@@ -20,7 +21,6 @@
#include "headless/grit/headless_lib_resources.h"
#include "headless/lib/browser/headless_browser_context_impl.h"
#include "headless/lib/browser/headless_browser_impl.h"
-#include "headless/lib/browser/headless_network_conditions.h"
#include "headless/lib/browser/headless_web_contents_impl.h"
#include "headless/public/devtools/domains/target.h"
#include "printing/units.h"
@@ -155,9 +155,11 @@ void OnBeginFrameFinished(
ImageEncoding encoding,
int quality,
bool has_damage,
+ bool main_frame_content_updated,
std::unique_ptr<SkBitmap> bitmap) {
auto result = base::MakeUnique<base::DictionaryValue>();
result->SetBoolean("hasDamage", has_damage);
+ result->SetBoolean("mainFrameContentUpdated", main_frame_content_updated);
if (bitmap && !bitmap->drawsNothing()) {
result->SetString("screenshotData",
@@ -182,21 +184,42 @@ std::unique_ptr<base::DictionaryValue> ParsePrintSettings(
HeadlessPrintSettings* settings) {
// We can safely ignore the return values of the following Get methods since
// the defaults are already set in |settings|.
- params->GetBoolean("landscape", &settings->landscape);
- params->GetBoolean("displayHeaderFooter", &settings->display_header_footer);
- params->GetBoolean("printBackground", &settings->should_print_backgrounds);
- params->GetDouble("scale", &settings->scale);
+ if (const base::Value* landscape_value = params->FindKey("landscape"))
+ settings->landscape = landscape_value->GetBool();
+
+ if (const base::Value* display_header_footer_value =
+ params->FindKey("displayHeaderFooter")) {
+ settings->display_header_footer = display_header_footer_value->GetBool();
+ }
+
+ if (const base::Value* should_print_backgrounds_value =
+ params->FindKey("printBackground")) {
+ settings->should_print_backgrounds =
+ should_print_backgrounds_value->GetBool();
+ }
+ if (const base::Value* scale_value = params->FindKey("scale"))
+ settings->scale = scale_value->GetDouble();
if (settings->scale > kScaleMaxVal / 100 ||
settings->scale < kScaleMinVal / 100)
return CreateInvalidParamResponse(command_id, "scale");
- params->GetString("pageRanges", &settings->page_ranges);
- params->GetBoolean("ignoreInvalidPageRanges",
- &settings->ignore_invalid_page_ranges);
+ if (const base::Value* page_ranges_value = params->FindKey("pageRanges"))
+ settings->page_ranges = page_ranges_value->GetString();
+
+ if (const base::Value* ignore_invalid_page_ranges_value =
+ params->FindKey("ignoreInvalidPageRanges")) {
+ settings->ignore_invalid_page_ranges =
+ ignore_invalid_page_ranges_value->GetBool();
+ }
double paper_width_in_inch = printing::kLetterWidthInch;
+
+ if (const base::Value* paper_width_value = params->FindKey("paperWidth"))
+ paper_width_in_inch = paper_width_value->GetDouble();
+
double paper_height_in_inch = printing::kLetterHeightInch;
- params->GetDouble("paperWidth", &paper_width_in_inch);
- params->GetDouble("paperHeight", &paper_height_in_inch);
+
+ if (const base::Value* paper_height_value = params->FindKey("paperHeight"))
+ paper_height_in_inch = paper_height_value->GetDouble();
if (paper_width_in_inch <= 0)
return CreateInvalidParamResponse(command_id, "paperWidth");
if (paper_height_in_inch <= 0)
@@ -211,10 +234,19 @@ std::unique_ptr<base::DictionaryValue> ParsePrintSettings(
double margin_bottom_in_inch = default_margin_in_inch;
double margin_left_in_inch = default_margin_in_inch;
double margin_right_in_inch = default_margin_in_inch;
- params->GetDouble("marginTop", &margin_top_in_inch);
- params->GetDouble("marginBottom", &margin_bottom_in_inch);
- params->GetDouble("marginLeft", &margin_left_in_inch);
- params->GetDouble("marginRight", &margin_right_in_inch);
+
+ if (const base::Value* margin_top_value = params->FindKey("marginTop"))
+ margin_top_in_inch = margin_top_value->GetDouble();
+
+ if (const base::Value* margin_bottom_value = params->FindKey("marginBottom"))
+ margin_bottom_in_inch = margin_bottom_value->GetDouble();
+
+ if (const base::Value* margin_left_value = params->FindKey("marginLeft"))
+ margin_left_in_inch = margin_left_value->GetDouble();
+
+ if (const base::Value* margin_right_value = params->FindKey("marginRight"))
+ margin_right_in_inch = margin_right_value->GetDouble();
+
if (margin_top_in_inch < 0)
return CreateInvalidParamResponse(command_id, "marginTop");
if (margin_bottom_in_inch < 0)
@@ -281,7 +313,7 @@ HeadlessDevToolsManagerDelegate::HeadlessDevToolsManagerDelegate(
&HeadlessDevToolsManagerDelegate::BeginFrame, base::Unretained(this));
}
-HeadlessDevToolsManagerDelegate::~HeadlessDevToolsManagerDelegate() {}
+HeadlessDevToolsManagerDelegate::~HeadlessDevToolsManagerDelegate() = default;
bool HeadlessDevToolsManagerDelegate::HandleCommand(
content::DevToolsAgentHost* agent_host,
@@ -292,21 +324,22 @@ bool HeadlessDevToolsManagerDelegate::HandleCommand(
if (!browser_)
return false;
- int id;
- std::string method;
- if (!command->GetInteger("id", &id) || !command->GetString("method", &method))
+ const base::Value* id_value = command->FindKey("id");
+ const base::Value* method_value = command->FindKey("method");
+ if (!id_value || !method_value)
return false;
const base::DictionaryValue* params = nullptr;
command->GetDictionary("params", &params);
+ const std::string& method = method_value->GetString();
auto find_it = command_map_.find(method);
if (find_it == command_map_.end()) {
// Check for any commands that are actioned then passed on to devtools to
// handle.
find_it = unhandled_command_map_.find(method);
if (find_it != unhandled_command_map_.end())
- find_it->second.Run(agent_host, session_id, id, params);
+ find_it->second.Run(agent_host, session_id, id_value->GetInt(), params);
return false;
}
@@ -315,7 +348,8 @@ bool HeadlessDevToolsManagerDelegate::HandleCommand(
agent_host->GetType() != content::DevToolsAgentHost::kTypeBrowser)
return false;
- auto cmd_result = find_it->second.Run(agent_host, session_id, id, params);
+ auto cmd_result =
+ find_it->second.Run(agent_host, session_id, id_value->GetInt(), params);
if (!cmd_result)
return false;
agent_host->SendProtocolMessageToClient(session_id,
@@ -333,18 +367,19 @@ bool HeadlessDevToolsManagerDelegate::HandleAsyncCommand(
if (!browser_)
return false;
- int id;
- std::string method;
- if (!command->GetInteger("id", &id) || !command->GetString("method", &method))
+ const base::Value* id_value = command->FindKey("id");
+ const base::Value* method_value = command->FindKey("method");
+ if (!id_value || !method_value)
return false;
- auto find_it = async_command_map_.find(method);
+ auto find_it = async_command_map_.find(method_value->GetString());
if (find_it == async_command_map_.end())
return false;
const base::DictionaryValue* params = nullptr;
command->GetDictionary("params", &params);
- find_it->second.Run(agent_host, session_id, id, params, callback);
+ find_it->second.Run(agent_host, session_id, id_value->GetInt(), params,
+ callback);
return true;
}
@@ -432,16 +467,32 @@ HeadlessDevToolsManagerDelegate::CreateTarget(
int command_id,
const base::DictionaryValue* params) {
std::string url;
+
+ if (const base::Value* url_value = params->FindKey("url")) {
+ url = url_value->GetString();
+ } else {
+ return CreateInvalidParamResponse(command_id, "url");
+ }
+
std::string browser_context_id;
+ if (const base::Value* browser_context_id_value =
+ params->FindKey("browserContextId")) {
+ browser_context_id = browser_context_id_value->GetString();
+ }
+
int width = browser_->options()->window_size.width();
+ if (const base::Value* width_value = params->FindKey("width"))
+ width = width_value->GetInt();
+
int height = browser_->options()->window_size.height();
- if (!params || !params->GetString("url", &url))
- return CreateInvalidParamResponse(command_id, "url");
+ if (const base::Value* height_value = params->FindKey("height"))
+ height = height_value->GetInt();
+
bool enable_begin_frame_control = false;
- params->GetString("browserContextId", &browser_context_id);
- params->GetInteger("width", &width);
- params->GetInteger("height", &height);
- params->GetBoolean("enableBeginFrameControl", &enable_begin_frame_control);
+ if (const base::Value* enable_begin_frame_control_value =
+ params->FindKey("enableBeginFrameControl")) {
+ enable_begin_frame_control = enable_begin_frame_control_value->GetBool();
+ }
#if defined(OS_MACOSX)
if (enable_begin_frame_control) {
@@ -488,11 +539,12 @@ HeadlessDevToolsManagerDelegate::CloseTarget(
int session_id,
int command_id,
const base::DictionaryValue* params) {
- std::string target_id;
- if (!params || !params->GetString("targetId", &target_id))
+ const base::Value* target_id_value = params->FindKey("targetId");
+ if (!target_id_value)
return CreateInvalidParamResponse(command_id, "targetId");
HeadlessWebContents* web_contents =
- browser_->GetWebContentsForDevToolsAgentHostId(target_id);
+ browser_->GetWebContentsForDevToolsAgentHostId(
+ target_id_value->GetString());
bool success = false;
if (web_contents) {
web_contents->Close();
@@ -528,11 +580,13 @@ HeadlessDevToolsManagerDelegate::DisposeBrowserContext(
int session_id,
int command_id,
const base::DictionaryValue* params) {
- std::string browser_context_id;
- if (!params || !params->GetString("browserContextId", &browser_context_id))
+ const base::Value* browser_context_id_value =
+ params->FindKey("browserContextId");
+ if (!browser_context_id_value)
return CreateInvalidParamResponse(command_id, "browserContextId");
+
HeadlessBrowserContext* context =
- browser_->GetBrowserContextForId(browser_context_id);
+ browser_->GetBrowserContextForId(browser_context_id_value->GetString());
bool success = false;
if (context && context != browser_->GetDefaultBrowserContext() &&
@@ -555,12 +609,13 @@ HeadlessDevToolsManagerDelegate::GetWindowForTarget(
int session_id,
int command_id,
const base::DictionaryValue* params) {
- std::string target_id;
- if (!params->GetString("targetId", &target_id))
+ const base::Value* target_id_value = params->FindKey("targetId");
+ if (!target_id_value)
return CreateInvalidParamResponse(command_id, "targetId");
HeadlessWebContentsImpl* web_contents = HeadlessWebContentsImpl::From(
- browser_->GetWebContentsForDevToolsAgentHostId(target_id));
+ browser_->GetWebContentsForDevToolsAgentHostId(
+ target_id_value->GetString()));
if (!web_contents) {
return CreateErrorResponse(command_id, kErrorServerError,
"No web contents for the given target id");
@@ -590,11 +645,11 @@ HeadlessDevToolsManagerDelegate::GetWindowBounds(
int session_id,
int command_id,
const base::DictionaryValue* params) {
- int window_id;
- if (!params->GetInteger("windowId", &window_id))
+ HeadlessWebContentsImpl* web_contents;
+ const base::Value* window_id_value = params->FindKey("windowId");
+ if (!window_id_value || !window_id_value->is_int())
return CreateInvalidParamResponse(command_id, "windowId");
- HeadlessWebContentsImpl* web_contents =
- browser_->GetWebContentsForWindowId(window_id);
+ web_contents = browser_->GetWebContentsForWindowId(window_id_value->GetInt());
if (!web_contents) {
return CreateErrorResponse(command_id, kErrorServerError,
"Browser window not found");
@@ -611,48 +666,56 @@ HeadlessDevToolsManagerDelegate::SetWindowBounds(
int session_id,
int command_id,
const base::DictionaryValue* params) {
- int window_id;
- if (!params->GetInteger("windowId", &window_id))
+ HeadlessWebContentsImpl* web_contents;
+ const base::Value* window_id_value = params->FindKey("windowId");
+ if (!window_id_value || !window_id_value->is_int())
return CreateInvalidParamResponse(command_id, "windowId");
- HeadlessWebContentsImpl* web_contents =
- browser_->GetWebContentsForWindowId(window_id);
+ web_contents = browser_->GetWebContentsForWindowId(window_id_value->GetInt());
+
if (!web_contents) {
return CreateErrorResponse(command_id, kErrorServerError,
"Browser window not found");
}
- const base::Value* value = nullptr;
- const base::DictionaryValue* bounds_dict = nullptr;
- if (!params->Get("bounds", &value) || !value->GetAsDictionary(&bounds_dict))
+ const base::Value* bounds_value = params->FindKey("bounds");
+ if (!bounds_value || !bounds_value->is_dict())
return CreateInvalidParamResponse(command_id, "bounds");
std::string window_state;
- if (!bounds_dict->GetString("windowState", &window_state)) {
+ if (const base::Value* window_state_value =
+ bounds_value->FindKey("windowState")) {
+ window_state = window_state_value->GetString();
+ if (window_state != "normal" && window_state != "minimized" &&
+ window_state != "maximized" && window_state != "fullscreen") {
+ return CreateInvalidParamResponse(command_id, "windowState");
+ }
+ } else {
window_state = "normal";
- } else if (window_state != "normal" && window_state != "minimized" &&
- window_state != "maximized" && window_state != "fullscreen") {
- return CreateInvalidParamResponse(command_id, "windowState");
}
// Compute updated bounds when window state is normal.
bool set_bounds = false;
gfx::Rect bounds = web_contents->web_contents()->GetContainerBounds();
- int left, top, width, height;
- if (bounds_dict->GetInteger("left", &left)) {
- bounds.set_x(left);
+ if (const base::Value* left_value = bounds_value->FindKey("left")) {
+ bounds.set_x(left_value->GetInt());
set_bounds = true;
}
- if (bounds_dict->GetInteger("top", &top)) {
- bounds.set_y(top);
+
+ if (const base::Value* top_value = bounds_value->FindKey("top")) {
+ bounds.set_y(top_value->GetInt());
set_bounds = true;
}
- if (bounds_dict->GetInteger("width", &width)) {
+
+ if (const base::Value* width_value = bounds_value->FindKey("width")) {
+ int width = width_value->GetInt();
if (width < 0)
return CreateInvalidParamResponse(command_id, "width");
bounds.set_width(width);
set_bounds = true;
}
- if (bounds_dict->GetInteger("height", &height)) {
+
+ if (const base::Value* height_value = bounds_value->FindKey("height")) {
+ int height = height_value->GetInt();
if (height < 0)
return CreateInvalidParamResponse(command_id, "height");
bounds.set_height(height);
@@ -685,19 +748,26 @@ HeadlessDevToolsManagerDelegate::EmulateNetworkConditions(
int command_id,
const base::DictionaryValue* params) {
// Associate NetworkConditions to context
- HeadlessBrowserContextImpl* browser_context =
- static_cast<HeadlessBrowserContextImpl*>(
- browser_->GetDefaultBrowserContext());
- bool offline = false;
- double latency = 0, download_throughput = 0, upload_throughput = 0;
- params->GetBoolean("offline", &offline);
- params->GetDouble("latency", &latency);
- params->GetDouble("downloadThroughput", &download_throughput);
- params->GetDouble("uploadThroughput", &upload_throughput);
+ std::vector<HeadlessBrowserContext*> browser_contexts =
+ browser_->GetAllBrowserContexts();
+ if (browser_contexts.empty())
+ return CreateSuccessResponse(command_id, nullptr);
+ const base::Value* offline_value = params->FindKey("offline");
+ const base::Value* latency_value = params->FindKey("latency");
+ const base::Value* download_throughput_value =
+ params->FindKey("downloadThroughput");
+ const base::Value* upload_throughput_value =
+ params->FindKey("uploadThroughput");
HeadlessNetworkConditions conditions(HeadlessNetworkConditions(
- offline, std::max(latency, 0.0), std::max(download_throughput, 0.0),
- std::max(upload_throughput, 0.0)));
- browser_context->SetNetworkConditions(conditions);
+ offline_value ? offline_value->GetBool() : false,
+ latency_value ? std::max(latency_value->GetDouble(), 0.0) : 0,
+ download_throughput_value
+ ? std::max(download_throughput_value->GetDouble(), 0.0)
+ : 0,
+ upload_throughput_value
+ ? std::max(upload_throughput_value->GetDouble(), 0.0)
+ : 0));
+ SetNetworkConditions(browser_contexts, conditions);
return CreateSuccessResponse(command_id, nullptr);
}
@@ -707,13 +777,26 @@ HeadlessDevToolsManagerDelegate::NetworkDisable(
int session_id,
int command_id,
const base::DictionaryValue* params) {
- HeadlessBrowserContextImpl* browser_context =
- static_cast<HeadlessBrowserContextImpl*>(
- browser_->GetDefaultBrowserContext());
- browser_context->SetNetworkConditions(HeadlessNetworkConditions());
+ std::vector<HeadlessBrowserContext*> browser_contexts =
+ browser_->GetAllBrowserContexts();
+ if (browser_contexts.empty())
+ return CreateSuccessResponse(command_id, nullptr);
+ SetNetworkConditions(browser_contexts, HeadlessNetworkConditions());
return CreateSuccessResponse(command_id, nullptr);
}
+void HeadlessDevToolsManagerDelegate::SetNetworkConditions(
+ std::vector<HeadlessBrowserContext*> browser_contexts,
+ HeadlessNetworkConditions conditions) {
+ for (std::vector<HeadlessBrowserContext*>::iterator it =
+ browser_contexts.begin();
+ it != browser_contexts.end(); ++it) {
+ HeadlessBrowserContextImpl* context =
+ static_cast<HeadlessBrowserContextImpl*>(*it);
+ context->SetNetworkConditions(conditions);
+ }
+}
+
std::unique_ptr<base::DictionaryValue>
HeadlessDevToolsManagerDelegate::EnableHeadlessExperimental(
content::DevToolsAgentHost* agent_host,
@@ -774,24 +857,21 @@ void HeadlessDevToolsManagerDelegate::BeginFrame(
return;
}
- double frame_time_double = 0;
- double deadline_double = 0;
- double interval_double = 0;
-
base::Time frame_time;
base::TimeTicks frame_timeticks;
base::TimeTicks deadline;
base::TimeDelta interval;
- if (params->GetDouble("frameTime", &frame_time_double)) {
- frame_time = base::Time::FromDoubleT(frame_time_double);
+ if (const base::Value* frame_time_value = params->FindKey("frameTime")) {
+ frame_time = base::Time::FromJsTime(frame_time_value->GetDouble());
base::TimeDelta delta = frame_time - base::Time::UnixEpoch();
frame_timeticks = base::TimeTicks::UnixEpoch() + delta;
} else {
frame_timeticks = base::TimeTicks::Now();
}
- if (params->GetDouble("interval", &interval_double)) {
+ if (const base::Value* interval_value = params->FindKey("interval")) {
+ double interval_double = interval_value->GetDouble();
if (interval_double <= 0) {
callback.Run(CreateErrorResponse(command_id, kErrorInvalidParam,
"interval has to be greater than 0"));
@@ -802,9 +882,9 @@ void HeadlessDevToolsManagerDelegate::BeginFrame(
interval = viz::BeginFrameArgs::DefaultInterval();
}
- if (params->GetDouble("deadline", &deadline_double)) {
+ if (const base::Value* deadline_value = params->FindKey("deadline")) {
base::TimeDelta delta =
- base::Time::FromDoubleT(deadline_double) - frame_time;
+ base::Time::FromDoubleT(deadline_value->GetDouble()) - frame_time;
if (delta <= base::TimeDelta()) {
callback.Run(CreateErrorResponse(command_id, kErrorInvalidParam,
"deadline has to be after frameTime"));
@@ -829,8 +909,8 @@ void HeadlessDevToolsManagerDelegate::BeginFrame(
capture_screenshot = true;
- std::string format;
- if (screenshot_dict->GetString("format", &format)) {
+ if (const base::Value* format_value = screenshot_dict->FindKey("format")) {
+ const std::string& format = format_value->GetString();
if (format == kPng) {
encoding = ImageEncoding::kPng;
} else if (format == kJpeg) {
@@ -842,12 +922,14 @@ void HeadlessDevToolsManagerDelegate::BeginFrame(
}
}
- if (screenshot_dict->GetInteger("quality", &quality) &&
- (quality < 0 || quality > 100)) {
- callback.Run(
- CreateErrorResponse(command_id, kErrorInvalidParam,
- "screenshot.quality has to be in range 0..100"));
- return;
+ if (const base::Value* quality_value = screenshot_dict->FindKey("quality")) {
+ quality = quality_value->GetInt();
+ if (quality < 0 || quality > 100) {
+ callback.Run(CreateErrorResponse(
+ command_id, kErrorInvalidParam,
+ "screenshot.quality has to be in range 0..100"));
+ return;
+ }
}
}
diff --git a/chromium/headless/lib/browser/headless_devtools_manager_delegate.h b/chromium/headless/lib/browser/headless_devtools_manager_delegate.h
index 9ad18ba1a07..b8fb8a81110 100644
--- a/chromium/headless/lib/browser/headless_devtools_manager_delegate.h
+++ b/chromium/headless/lib/browser/headless_devtools_manager_delegate.h
@@ -13,6 +13,8 @@
#include "base/memory/weak_ptr.h"
#include "base/values.h"
#include "content/public/browser/devtools_manager_delegate.h"
+#include "headless/lib/browser/headless_network_conditions.h"
+#include "headless/public/headless_browser_context.h"
#include "printing/features/features.h"
#if BUILDFLAG(ENABLE_BASIC_PRINTING)
@@ -116,6 +118,10 @@ class HeadlessDevToolsManagerDelegate
int command_id,
const base::DictionaryValue* params);
+ void SetNetworkConditions(
+ std::vector<HeadlessBrowserContext*> browser_contexts,
+ HeadlessNetworkConditions conditions);
+
void PrintToPDF(content::DevToolsAgentHost* agent_host,
int session_id,
int command_id,
diff --git a/chromium/headless/lib/browser/headless_focus_client.cc b/chromium/headless/lib/browser/headless_focus_client.cc
index 9a4e806e86c..c406c703861 100644
--- a/chromium/headless/lib/browser/headless_focus_client.cc
+++ b/chromium/headless/lib/browser/headless_focus_client.cc
@@ -12,7 +12,7 @@ namespace headless {
HeadlessFocusClient::HeadlessFocusClient()
: focused_window_(NULL), observer_manager_(this) {}
-HeadlessFocusClient::~HeadlessFocusClient() {}
+HeadlessFocusClient::~HeadlessFocusClient() = default;
void HeadlessFocusClient::AddObserver(
aura::client::FocusChangeObserver* observer) {
diff --git a/chromium/headless/lib/browser/headless_network_conditions.cc b/chromium/headless/lib/browser/headless_network_conditions.cc
index 3b7c643569e..208208c9020 100644
--- a/chromium/headless/lib/browser/headless_network_conditions.cc
+++ b/chromium/headless/lib/browser/headless_network_conditions.cc
@@ -21,6 +21,6 @@ HeadlessNetworkConditions::HeadlessNetworkConditions(bool offline,
download_throughput(download_throughput),
upload_throughput(upload_throughput) {}
-HeadlessNetworkConditions::~HeadlessNetworkConditions() {}
+HeadlessNetworkConditions::~HeadlessNetworkConditions() = default;
} // namespace headless
diff --git a/chromium/headless/lib/browser/headless_network_delegate.cc b/chromium/headless/lib/browser/headless_network_delegate.cc
index 28859f5f686..51e39600211 100644
--- a/chromium/headless/lib/browser/headless_network_delegate.cc
+++ b/chromium/headless/lib/browser/headless_network_delegate.cc
@@ -40,7 +40,9 @@ int HeadlessNetworkDelegate::OnBeforeURLRequest(
net::URLRequest* request,
const net::CompletionCallback& callback,
GURL* new_url) {
- if (headless_browser_context_->ShouldRemoveHeaders()) {
+ base::AutoLock lock(lock_);
+ if (headless_browser_context_ &&
+ headless_browser_context_->ShouldRemoveHeaders()) {
request->RemoveRequestHeaderByName(
kDevToolsEmulateNetworkConditionsClientId);
}
@@ -112,7 +114,7 @@ bool HeadlessNetworkDelegate::OnCanGetCookies(
}
bool HeadlessNetworkDelegate::OnCanSetCookie(const net::URLRequest& request,
- const std::string& cookie_line,
+ const net::CanonicalCookie& cookie,
net::CookieOptions* options) {
return true;
}
diff --git a/chromium/headless/lib/browser/headless_network_delegate.h b/chromium/headless/lib/browser/headless_network_delegate.h
index ccfb5f63976..66ed81528fc 100644
--- a/chromium/headless/lib/browser/headless_network_delegate.h
+++ b/chromium/headless/lib/browser/headless_network_delegate.h
@@ -66,7 +66,7 @@ class HeadlessNetworkDelegate : public net::NetworkDelegateImpl,
const net::CookieList& cookie_list) override;
bool OnCanSetCookie(const net::URLRequest& request,
- const std::string& cookie_line,
+ const net::CanonicalCookie& cookie,
net::CookieOptions* options) override;
bool OnCanAccessFile(const net::URLRequest& request,
diff --git a/chromium/headless/lib/browser/headless_permission_manager.cc b/chromium/headless/lib/browser/headless_permission_manager.cc
index a75ee4c3481..8f0fc66e483 100644
--- a/chromium/headless/lib/browser/headless_permission_manager.cc
+++ b/chromium/headless/lib/browser/headless_permission_manager.cc
@@ -5,13 +5,16 @@
#include "headless/lib/browser/headless_permission_manager.h"
#include "base/callback.h"
+#include "content/public/browser/browser_context.h"
#include "content/public/browser/permission_type.h"
namespace headless {
-HeadlessPermissionManager::HeadlessPermissionManager() : PermissionManager() {}
+HeadlessPermissionManager::HeadlessPermissionManager(
+ content::BrowserContext* browser_context)
+ : browser_context_(browser_context) {}
-HeadlessPermissionManager::~HeadlessPermissionManager() {}
+HeadlessPermissionManager::~HeadlessPermissionManager() = default;
int HeadlessPermissionManager::RequestPermission(
content::PermissionType permission,
@@ -20,7 +23,14 @@ int HeadlessPermissionManager::RequestPermission(
bool user_gesture,
const base::Callback<void(blink::mojom::PermissionStatus)>& callback) {
// In headless mode we just pretent the user "closes" any permission prompt,
- // without accepting or denying.
+ // without accepting or denying. Notifications are the exception to this,
+ // which are explicitly disabled in Incognito mode.
+ if (browser_context_->IsOffTheRecord() &&
+ permission == content::PermissionType::NOTIFICATIONS) {
+ callback.Run(blink::mojom::PermissionStatus::DENIED);
+ return kNoPendingOperation;
+ }
+
callback.Run(blink::mojom::PermissionStatus::ASK);
return kNoPendingOperation;
}
diff --git a/chromium/headless/lib/browser/headless_permission_manager.h b/chromium/headless/lib/browser/headless_permission_manager.h
index 5eb31859cc3..f9a12522333 100644
--- a/chromium/headless/lib/browser/headless_permission_manager.h
+++ b/chromium/headless/lib/browser/headless_permission_manager.h
@@ -9,11 +9,15 @@
#include "base/macros.h"
#include "content/public/browser/permission_manager.h"
+namespace content {
+class BrowserContext;
+}
+
namespace headless {
class HeadlessPermissionManager : public content::PermissionManager {
public:
- HeadlessPermissionManager();
+ explicit HeadlessPermissionManager(content::BrowserContext* browser_context);
~HeadlessPermissionManager() override;
// PermissionManager implementation.
@@ -49,6 +53,8 @@ class HeadlessPermissionManager : public content::PermissionManager {
void UnsubscribePermissionStatusChange(int subscription_id) override;
private:
+ content::BrowserContext* browser_context_;
+
DISALLOW_COPY_AND_ASSIGN(HeadlessPermissionManager);
};
diff --git a/chromium/headless/lib/browser/headless_platform_event_source.cc b/chromium/headless/lib/browser/headless_platform_event_source.cc
index 7c3100ee4f2..598ec80d1cb 100644
--- a/chromium/headless/lib/browser/headless_platform_event_source.cc
+++ b/chromium/headless/lib/browser/headless_platform_event_source.cc
@@ -6,8 +6,8 @@
namespace headless {
-HeadlessPlatformEventSource::HeadlessPlatformEventSource() {}
+HeadlessPlatformEventSource::HeadlessPlatformEventSource() = default;
-HeadlessPlatformEventSource::~HeadlessPlatformEventSource() {}
+HeadlessPlatformEventSource::~HeadlessPlatformEventSource() = default;
} // namespace headless
diff --git a/chromium/headless/lib/browser/headless_print_manager.cc b/chromium/headless/lib/browser/headless_print_manager.cc
index 2213796df0c..e7a816dcb45 100644
--- a/chromium/headless/lib/browser/headless_print_manager.cc
+++ b/chromium/headless/lib/browser/headless_print_manager.cc
@@ -51,7 +51,7 @@ HeadlessPrintManager::HeadlessPrintManager(content::WebContents* web_contents)
Reset();
}
-HeadlessPrintManager::~HeadlessPrintManager() {}
+HeadlessPrintManager::~HeadlessPrintManager() = default;
// static
std::string HeadlessPrintManager::PrintResultToString(PrintResult result) {
diff --git a/chromium/headless/lib/browser/headless_quota_permission_context.cc b/chromium/headless/lib/browser/headless_quota_permission_context.cc
index 618cbe5c978..b75f01bcf42 100644
--- a/chromium/headless/lib/browser/headless_quota_permission_context.cc
+++ b/chromium/headless/lib/browser/headless_quota_permission_context.cc
@@ -8,7 +8,7 @@
namespace headless {
-HeadlessQuotaPermissionContext::HeadlessQuotaPermissionContext() {}
+HeadlessQuotaPermissionContext::HeadlessQuotaPermissionContext() = default;
void HeadlessQuotaPermissionContext::RequestQuotaPermission(
const content::StorageQuotaParams& params,
@@ -24,6 +24,6 @@ void HeadlessQuotaPermissionContext::RequestQuotaPermission(
callback.Run(QUOTA_PERMISSION_RESPONSE_ALLOW);
}
-HeadlessQuotaPermissionContext::~HeadlessQuotaPermissionContext() {}
+HeadlessQuotaPermissionContext::~HeadlessQuotaPermissionContext() = default;
} // namespace headless
diff --git a/chromium/headless/lib/browser/headless_resource_dispatcher_host_delegate.cc b/chromium/headless/lib/browser/headless_resource_dispatcher_host_delegate.cc
index b8feeb5a386..d7b4888c527 100644
--- a/chromium/headless/lib/browser/headless_resource_dispatcher_host_delegate.cc
+++ b/chromium/headless/lib/browser/headless_resource_dispatcher_host_delegate.cc
@@ -6,16 +6,10 @@
namespace headless {
-HeadlessResourceDispatcherHostDelegate::HeadlessResourceDispatcherHostDelegate(
- bool enable_resource_scheduler)
- : enable_resource_scheduler_(enable_resource_scheduler) {}
-
HeadlessResourceDispatcherHostDelegate::
- ~HeadlessResourceDispatcherHostDelegate() {}
+ HeadlessResourceDispatcherHostDelegate() = default;
-bool HeadlessResourceDispatcherHostDelegate::ShouldUseResourceScheduler()
- const {
- return enable_resource_scheduler_;
-}
+HeadlessResourceDispatcherHostDelegate::
+ ~HeadlessResourceDispatcherHostDelegate() = default;
} // namespace headless
diff --git a/chromium/headless/lib/browser/headless_resource_dispatcher_host_delegate.h b/chromium/headless/lib/browser/headless_resource_dispatcher_host_delegate.h
index 8e276489478..7600c9bfe05 100644
--- a/chromium/headless/lib/browser/headless_resource_dispatcher_host_delegate.h
+++ b/chromium/headless/lib/browser/headless_resource_dispatcher_host_delegate.h
@@ -12,15 +12,10 @@ namespace headless {
class HeadlessResourceDispatcherHostDelegate
: public content::ResourceDispatcherHostDelegate {
public:
- explicit HeadlessResourceDispatcherHostDelegate(
- bool enable_resource_scheduler);
+ HeadlessResourceDispatcherHostDelegate();
~HeadlessResourceDispatcherHostDelegate() override;
- bool ShouldUseResourceScheduler() const override;
-
private:
- const bool enable_resource_scheduler_;
-
DISALLOW_COPY_AND_ASSIGN(HeadlessResourceDispatcherHostDelegate);
};
diff --git a/chromium/headless/lib/browser/headless_screen.cc b/chromium/headless/lib/browser/headless_screen.cc
index 1ee6b64a754..7ce5366b3fa 100644
--- a/chromium/headless/lib/browser/headless_screen.cc
+++ b/chromium/headless/lib/browser/headless_screen.cc
@@ -21,7 +21,7 @@ HeadlessScreen* HeadlessScreen::Create(const gfx::Size& size) {
return new HeadlessScreen(gfx::Rect(size));
}
-HeadlessScreen::~HeadlessScreen() {}
+HeadlessScreen::~HeadlessScreen() = default;
gfx::Point HeadlessScreen::GetCursorScreenPoint() {
return aura::Env::GetInstance()->last_mouse_location();
diff --git a/chromium/headless/lib/browser/headless_tab_socket_impl.cc b/chromium/headless/lib/browser/headless_tab_socket_impl.cc
index 05e562c4099..cbdb7dc5d79 100644
--- a/chromium/headless/lib/browser/headless_tab_socket_impl.cc
+++ b/chromium/headless/lib/browser/headless_tab_socket_impl.cc
@@ -19,7 +19,7 @@ HeadlessTabSocketImpl::HeadlessTabSocketImpl(content::WebContents* web_contents)
listener_(nullptr),
weak_ptr_factory_(this) {}
-HeadlessTabSocketImpl::~HeadlessTabSocketImpl() {}
+HeadlessTabSocketImpl::~HeadlessTabSocketImpl() = default;
// Wrangles the async responses to
// HeadlessRenderFrameControllerImpl::InstallTabSocket for which at most one
@@ -68,7 +68,7 @@ class TabSocketInstallationController
bool success_;
friend class base::RefCounted<TabSocketInstallationController>;
- ~TabSocketInstallationController() {}
+ ~TabSocketInstallationController() = default;
};
void HeadlessTabSocketImpl::InstallHeadlessTabSocketBindings(
diff --git a/chromium/headless/lib/browser/headless_url_request_context_getter.cc b/chromium/headless/lib/browser/headless_url_request_context_getter.cc
index d2eaffad364..9df02187acc 100644
--- a/chromium/headless/lib/browser/headless_url_request_context_getter.cc
+++ b/chromium/headless/lib/browser/headless_url_request_context_getter.cc
@@ -10,18 +10,31 @@
#include "base/memory/ptr_util.h"
#include "base/task_scheduler/post_task.h"
+#include "build/build_config.h"
+#include "components/cookie_config/cookie_store_util.h"
#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/cookie_store_factory.h"
#include "content/public/browser/devtools_network_transaction_factory.h"
+#include "headless/app/headless_shell_switches.h"
#include "headless/lib/browser/headless_browser_context_impl.h"
#include "headless/lib/browser/headless_browser_context_options.h"
#include "headless/lib/browser/headless_network_delegate.h"
+#include "net/cookies/cookie_store.h"
#include "net/dns/mapped_host_resolver.h"
#include "net/http/http_transaction_factory.h"
#include "net/http/http_util.h"
#include "net/proxy/proxy_service.h"
+#include "net/ssl/channel_id_service.h"
+#include "net/ssl/default_channel_id_store.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_builder.h"
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+#include "base/command_line.h"
+#include "components/os_crypt/key_storage_config_linux.h"
+#include "components/os_crypt/os_crypt.h"
+#endif
+
namespace headless {
HeadlessURLRequestContextGetter::HeadlessURLRequestContextGetter(
@@ -74,6 +87,51 @@ HeadlessURLRequestContextGetter::GetURLRequestContext() {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
if (!url_request_context_) {
net::URLRequestContextBuilder builder;
+
+ {
+ base::AutoLock lock(lock_);
+ // Don't store cookies in incognito mode or if no user-data-dir was
+ // specified
+ // TODO: Enable this always once saving/restoring sessions is implemented
+ // (https://crbug.com/617931)
+ if (headless_browser_context_ &&
+ !headless_browser_context_->IsOffTheRecord() &&
+ !headless_browser_context_->options()->user_data_dir().empty()) {
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+ std::unique_ptr<os_crypt::Config> config(new os_crypt::Config());
+ base::CommandLine* command_line =
+ base::CommandLine::ForCurrentProcess();
+ config->store =
+ command_line->GetSwitchValueASCII(switches::kPasswordStore);
+ config->product_name = "HeadlessChrome";
+ // OSCrypt may target keyring, which requires calls from the main
+ // thread.
+ config->main_thread_runner =
+ content::BrowserThread::GetTaskRunnerForThread(
+ content::BrowserThread::UI);
+ config->should_use_preference = false;
+ config->user_data_path = headless_browser_context_->GetPath();
+ OSCrypt::SetConfig(std::move(config));
+#endif
+
+ content::CookieStoreConfig cookie_config(
+ headless_browser_context_->GetPath().Append(
+ FILE_PATH_LITERAL("Cookies")),
+ content::CookieStoreConfig::PERSISTANT_SESSION_COOKIES, NULL);
+ cookie_config.crypto_delegate =
+ cookie_config::GetCookieCryptoDelegate();
+ std::unique_ptr<net::CookieStore> cookie_store =
+ CreateCookieStore(cookie_config);
+ std::unique_ptr<net::ChannelIDService> channel_id_service =
+ base::MakeUnique<net::ChannelIDService>(
+ new net::DefaultChannelIDStore(nullptr));
+
+ cookie_store->SetChannelIDServiceID(channel_id_service->GetUniqueID());
+ builder.SetCookieAndChannelIdStores(std::move(cookie_store),
+ std::move(channel_id_service));
+ }
+ }
+
builder.set_accept_language(
net::HttpUtil::GenerateAcceptLanguageHeader(accept_language_));
builder.set_user_agent(user_agent_);
diff --git a/chromium/headless/lib/browser/headless_url_request_context_getter.h b/chromium/headless/lib/browser/headless_url_request_context_getter.h
index 92341b3b497..1fb71c9f48a 100644
--- a/chromium/headless/lib/browser/headless_url_request_context_getter.h
+++ b/chromium/headless/lib/browser/headless_url_request_context_getter.h
@@ -14,7 +14,6 @@
#include "base/memory/ref_counted.h"
#include "base/single_thread_task_runner.h"
#include "content/public/browser/browser_context.h"
-#include "content/public/browser/content_browser_client.h"
#include "headless/public/headless_browser.h"
#include "net/proxy/proxy_config.h"
#include "net/proxy/proxy_config_service.h"
diff --git a/chromium/headless/lib/browser/headless_web_contents_impl.cc b/chromium/headless/lib/browser/headless_web_contents_impl.cc
index a3d53b70e4d..cc0c7509625 100644
--- a/chromium/headless/lib/browser/headless_web_contents_impl.cc
+++ b/chromium/headless/lib/browser/headless_web_contents_impl.cc
@@ -207,13 +207,13 @@ void CreateTabSocketMojoServiceForContents(
struct HeadlessWebContentsImpl::PendingFrame {
public:
- PendingFrame() {}
- ~PendingFrame() {}
+ PendingFrame() = default;
+ ~PendingFrame() = default;
bool MaybeRunCallback() {
if (wait_for_copy_result || !display_did_finish_frame)
return false;
- callback.Run(has_damage, std::move(bitmap));
+ callback.Run(has_damage, main_frame_content_updated, std::move(bitmap));
return true;
}
@@ -221,6 +221,7 @@ struct HeadlessWebContentsImpl::PendingFrame {
bool wait_for_copy_result = false;
bool display_did_finish_frame = false;
bool has_damage = false;
+ bool main_frame_content_updated = false;
std::unique_ptr<SkBitmap> bitmap;
FrameFinishedCallback callback;
@@ -321,6 +322,7 @@ HeadlessWebContentsImpl::HeadlessWebContentsImpl(
weak_ptr_factory_(this) {
#if BUILDFLAG(ENABLE_BASIC_PRINTING) && !defined(CHROME_MULTIPLE_DLL_CHILD)
HeadlessPrintManager::CreateForWebContents(web_contents);
+// TODO(weili): Add support for printing OOPIFs.
#endif
web_contents->GetMutableRendererPrefs()->accept_languages =
browser_context->options()->accept_language();
@@ -595,6 +597,14 @@ void HeadlessWebContentsImpl::DidReceiveCompositorFrame() {
for (int session_id : begin_frame_events_enabled_sessions_)
agent_host_->SendProtocolMessageToClient(session_id, json_result);
}
+
+ // Set main_frame_content_updated on pending frames that the display hasn't
+ // completed yet. Pending frames that it did complete won't incorporate this
+ // CompositorFrame. In practice, this should only be a single PendingFrame.
+ for (const std::unique_ptr<PendingFrame>& pending_frame : pending_frames_) {
+ if (!pending_frame->display_did_finish_frame)
+ pending_frame->main_frame_content_updated = true;
+ }
}
void HeadlessWebContentsImpl::PendingFrameReadbackComplete(
@@ -699,7 +709,7 @@ HeadlessWebContents* HeadlessWebContents::Builder::Build() {
return browser_context_->CreateWebContents(this);
}
-HeadlessWebContents::Builder::MojoService::MojoService() {}
+HeadlessWebContents::Builder::MojoService::MojoService() = default;
HeadlessWebContents::Builder::MojoService::MojoService(
const MojoService& other) = default;
@@ -709,6 +719,6 @@ HeadlessWebContents::Builder::MojoService::MojoService(
const ServiceFactoryCallback& service_factory)
: service_name(service_name), service_factory(service_factory) {}
-HeadlessWebContents::Builder::MojoService::~MojoService() {}
+HeadlessWebContents::Builder::MojoService::~MojoService() = default;
} // namespace headless
diff --git a/chromium/headless/lib/browser/headless_web_contents_impl.h b/chromium/headless/lib/browser/headless_web_contents_impl.h
index a2095d73f56..a4e06e025ae 100644
--- a/chromium/headless/lib/browser/headless_web_contents_impl.h
+++ b/chromium/headless/lib/browser/headless_web_contents_impl.h
@@ -143,7 +143,9 @@ class HEADLESS_EXPORT HeadlessWebContentsImpl
void SetBeginFrameEventsEnabled(int session_id, bool enabled);
using FrameFinishedCallback =
- base::Callback<void(bool /*has_damage*/, std::unique_ptr<SkBitmap>)>;
+ base::Callback<void(bool /* has_damage */,
+ bool /* main_frame_content_updated */,
+ std::unique_ptr<SkBitmap>)>;
void BeginFrame(const base::TimeTicks& frame_timeticks,
const base::TimeTicks& deadline,
const base::TimeDelta& interval,
@@ -170,7 +172,7 @@ class HEADLESS_EXPORT HeadlessWebContentsImpl
const SkBitmap& bitmap,
content::ReadbackResponse response);
- uint32_t begin_frame_source_id_ = viz::BeginFrameArgs::kManualSourceId;
+ uint64_t begin_frame_source_id_ = viz::BeginFrameArgs::kManualSourceId;
uint64_t begin_frame_sequence_number_ =
viz::BeginFrameArgs::kStartingFrameNumber;
bool begin_frame_control_enabled_ = false;
diff --git a/chromium/headless/lib/dom_tree_extraction_expected_nodes.txt b/chromium/headless/lib/dom_tree_extraction_expected_nodes.txt
index 5799ee09c77..588c8b05493 100644
--- a/chromium/headless/lib/dom_tree_extraction_expected_nodes.txt
+++ b/chromium/headless/lib/dom_tree_extraction_expected_nodes.txt
@@ -237,8 +237,8 @@
"boundingBox": {
"height": 200.0,
"width": 400.0,
- "x": 10.0,
- "y": 63.0
+ "x": 0.0,
+ "y": 0.0
},
"childNodeIndexes": [ 20, 21 ],
"frameId": "?",
@@ -259,8 +259,8 @@
"boundingBox": {
"height": 171.0,
"width": 384.0,
- "x": 18.0,
- "y": 71.0
+ "x": 8.0,
+ "y": 8.0
},
"childNodeIndexes": [ 22, 23, 25 ],
"layoutNodeIndex": 9,
@@ -280,8 +280,8 @@
"boundingBox": {
"height": 37.0,
"width": 384.0,
- "x": 18.0,
- "y": 71.0
+ "x": 8.0,
+ "y": 8.0
},
"childNodeIndexes": [ 24 ],
"layoutNodeIndex": 10,
@@ -520,10 +520,10 @@
{
"backendNodeId": 42,
"boundingBox": {
- "height": 0.0,
+ "height": 17.0,
"width": 0.0,
- "x": 0.0,
- "y": 0.0
+ "x": 8.0,
+ "y": 329.0
},
"inlineTextNodes": [ {
"boundingBox": {
diff --git a/chromium/headless/lib/frame_id_browsertest.cc b/chromium/headless/lib/frame_id_browsertest.cc
index c2b834938cd..11253124ac4 100644
--- a/chromium/headless/lib/frame_id_browsertest.cc
+++ b/chromium/headless/lib/frame_id_browsertest.cc
@@ -3,7 +3,6 @@
// found in the LICENSE file.
#include "base/bind.h"
-#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "content/public/test/browser_test.h"
@@ -11,6 +10,7 @@
#include "headless/lib/browser/headless_web_contents_impl.h"
#include "headless/public/devtools/domains/network.h"
#include "headless/public/devtools/domains/page.h"
+#include "headless/public/devtools/domains/runtime.h"
#include "headless/public/headless_devtools_client.h"
#include "headless/public/util/testing/test_in_memory_protocol_handler.h"
#include "headless/test/headless_browser_test.h"
@@ -70,7 +70,8 @@ const char* kStyle3Css = R"(
class FrameIdTest : public HeadlessAsyncDevTooledBrowserTest,
public network::ExperimentalObserver,
- public page::Observer {
+ public page::Observer,
+ public runtime::Observer {
public:
void RunDevTooledTest() override {
http_handler_->SetHeadlessBrowserContext(browser_context_);
@@ -78,23 +79,38 @@ class FrameIdTest : public HeadlessAsyncDevTooledBrowserTest,
EXPECT_TRUE(embedded_test_server()->Start());
devtools_client_->GetNetwork()->GetExperimental()->AddObserver(this);
devtools_client_->GetNetwork()->Enable();
+ devtools_client_->GetRuntime()->GetExperimental()->AddObserver(this);
+ devtools_client_->GetRuntime()->Enable();
+
+ // Enabling the runtime domain will send us the current context.
+ run_loop_ = base::MakeUnique<base::RunLoop>(
+ base::RunLoop::Type::kNestableTasksAllowed);
+ run_loop_->Run();
+ run_loop_ = nullptr;
+
+ EXPECT_EQ(1u, execution_context_frame_ids_.size());
+ execution_context_frame_ids_.clear();
if (EnableInterception()) {
- devtools_client_->GetNetwork()
- ->GetExperimental()
- ->SetRequestInterceptionEnabled(
- network::SetRequestInterceptionEnabledParams::Builder()
- .SetEnabled(true)
- .Build());
+ std::unique_ptr<headless::network::RequestPattern> match_all =
+ headless::network::RequestPattern::Builder()
+ .SetUrlPattern("*")
+ .Build();
+ std::vector<std::unique_ptr<headless::network::RequestPattern>> patterns;
+ patterns.push_back(std::move(match_all));
+ devtools_client_->GetNetwork()->GetExperimental()->SetRequestInterception(
+ network::SetRequestInterceptionParams::Builder()
+ .SetPatterns(std::move(patterns))
+ .Build());
}
devtools_client_->GetPage()->AddObserver(this);
- base::RunLoop run_loop;
- devtools_client_->GetPage()->Enable(run_loop.QuitClosure());
- base::MessageLoop::ScopedNestableTaskAllower nest_loop(
- base::MessageLoop::current());
- run_loop.Run();
+ run_loop_ = base::MakeUnique<base::RunLoop>(
+ base::RunLoop::Type::kNestableTasksAllowed);
+ devtools_client_->GetPage()->Enable(run_loop_->QuitClosure());
+ run_loop_->Run();
+ run_loop_ = nullptr;
devtools_client_->GetPage()->Navigate("http://foo.com/index.html");
}
@@ -103,7 +119,7 @@ class FrameIdTest : public HeadlessAsyncDevTooledBrowserTest,
ProtocolHandlerMap protocol_handlers;
std::unique_ptr<TestInMemoryProtocolHandler> http_handler(
new TestInMemoryProtocolHandler(browser()->BrowserIOThread(),
- /* simulate_slow_fetch */ false));
+ /* request_deferrer */ nullptr));
http_handler_ = http_handler.get();
http_handler_->InsertResponse("http://foo.com/index.html",
{kIndexHtml, "text/html"});
@@ -125,20 +141,37 @@ class FrameIdTest : public HeadlessAsyncDevTooledBrowserTest,
void OnRequestWillBeSent(
const network::RequestWillBeSentParams& params) override {
url_to_frame_id_[params.GetRequest()->GetUrl()] = params.GetFrameId();
+ frame_ids_.insert(params.GetFrameId());
}
// page::Observer implementation:
void OnLoadEventFired(const page::LoadEventFiredParams& params) override {
EXPECT_THAT(url_to_frame_id_,
ContainerEq(http_handler_->url_to_devtools_frame_id()));
+ EXPECT_THAT(execution_context_frame_ids_, ContainerEq(frame_ids_));
FinishAsynchronousTest();
}
virtual bool EnableInterception() const { return false; }
+ void OnExecutionContextCreated(
+ const runtime::ExecutionContextCreatedParams& params) override {
+ const base::Value* frameId =
+ params.GetContext()->GetAuxData()->FindKey("frameId");
+ if (frameId && frameId->is_string())
+ execution_context_frame_ids_.insert(frameId->GetString());
+
+ // If we're nested then exit.
+ if (run_loop_)
+ run_loop_->Quit();
+ }
+
private:
+ std::set<std::string> frame_ids_;
+ std::set<std::string> execution_context_frame_ids_;
std::map<std::string, std::string> url_to_frame_id_;
TestInMemoryProtocolHandler* http_handler_; // NOT OWNED
+ std::unique_ptr<base::RunLoop> run_loop_;
};
HEADLESS_ASYNC_DEVTOOLED_TEST_F(FrameIdTest);
diff --git a/chromium/headless/lib/headless_browser_browsertest.cc b/chromium/headless/lib/headless_browser_browsertest.cc
index 595b75aa7e2..0ed54b6303a 100644
--- a/chromium/headless/lib/headless_browser_browsertest.cc
+++ b/chromium/headless/lib/headless_browser_browsertest.cc
@@ -279,12 +279,11 @@ IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, HttpProtocolHandler) {
EXPECT_TRUE(web_contents);
EXPECT_TRUE(WaitForLoad(web_contents));
- std::string inner_html;
- EXPECT_TRUE(EvaluateScript(web_contents, "document.body.innerHTML")
- ->GetResult()
- ->GetValue()
- ->GetAsString(&inner_html));
- EXPECT_EQ(kResponseBody, inner_html);
+ EXPECT_EQ(kResponseBody,
+ EvaluateScript(web_contents, "document.body.innerHTML")
+ ->GetResult()
+ ->GetValue()
+ ->GetString());
}
IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, HttpsProtocolHandler) {
@@ -309,11 +308,11 @@ IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, HttpsProtocolHandler) {
EXPECT_TRUE(WaitForLoad(web_contents));
std::string inner_html;
- EXPECT_TRUE(EvaluateScript(web_contents, "document.body.innerHTML")
- ->GetResult()
- ->GetValue()
- ->GetAsString(&inner_html));
- EXPECT_EQ(kResponseBody, inner_html);
+ EXPECT_EQ(kResponseBody,
+ EvaluateScript(web_contents, "document.body.innerHTML")
+ ->GetResult()
+ ->GetValue()
+ ->GetString());
}
IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, WebGLSupported) {
@@ -323,15 +322,13 @@ IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, WebGLSupported) {
HeadlessWebContents* web_contents =
browser_context->CreateWebContentsBuilder().Build();
- bool webgl_supported;
EXPECT_TRUE(
EvaluateScript(web_contents,
"(document.createElement('canvas').getContext('webgl')"
" instanceof WebGLRenderingContext)")
->GetResult()
->GetValue()
- ->GetAsBoolean(&webgl_supported));
- EXPECT_TRUE(webgl_supported);
+ ->GetBool());
}
IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, ClipboardCopyPasteText) {
@@ -364,36 +361,30 @@ IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, DefaultSizes) {
HeadlessBrowser::Options::Builder builder;
const HeadlessBrowser::Options kDefaultOptions = builder.Build();
- int screen_width;
- int screen_height;
- int window_width;
- int window_height;
-
- EXPECT_TRUE(EvaluateScript(web_contents, "screen.width")
- ->GetResult()
- ->GetValue()
- ->GetAsInteger(&screen_width));
- EXPECT_TRUE(EvaluateScript(web_contents, "screen.height")
- ->GetResult()
- ->GetValue()
- ->GetAsInteger(&screen_height));
- EXPECT_TRUE(EvaluateScript(web_contents, "window.innerWidth")
- ->GetResult()
- ->GetValue()
- ->GetAsInteger(&window_width));
- EXPECT_TRUE(EvaluateScript(web_contents, "window.innerHeight")
- ->GetResult()
- ->GetValue()
- ->GetAsInteger(&window_height));
-
#if !defined(OS_MACOSX)
// On Mac headless does not override the screen dimensions, so they are
// left with the actual screen values.
- EXPECT_EQ(kDefaultOptions.window_size.width(), screen_width);
- EXPECT_EQ(kDefaultOptions.window_size.height(), screen_height);
+ EXPECT_EQ(kDefaultOptions.window_size.width(),
+ EvaluateScript(web_contents, "screen.width")
+ ->GetResult()
+ ->GetValue()
+ ->GetInt());
+ EXPECT_EQ(kDefaultOptions.window_size.height(),
+ EvaluateScript(web_contents, "screen.height")
+ ->GetResult()
+ ->GetValue()
+ ->GetInt());
#endif // !defined(OS_MACOSX)
- EXPECT_EQ(kDefaultOptions.window_size.width(), window_width);
- EXPECT_EQ(kDefaultOptions.window_size.height(), window_height);
+ EXPECT_EQ(kDefaultOptions.window_size.width(),
+ EvaluateScript(web_contents, "window.innerWidth")
+ ->GetResult()
+ ->GetValue()
+ ->GetInt());
+ EXPECT_EQ(kDefaultOptions.window_size.height(),
+ EvaluateScript(web_contents, "window.innerHeight")
+ ->GetResult()
+ ->GetValue()
+ ->GetInt());
}
namespace {
@@ -402,7 +393,7 @@ class ProtocolHandlerWithCookies
: public net::URLRequestJobFactory::ProtocolHandler {
public:
explicit ProtocolHandlerWithCookies(net::CookieList* sent_cookies);
- ~ProtocolHandlerWithCookies() override {}
+ ~ProtocolHandlerWithCookies() override = default;
net::URLRequestJob* MaybeCreateJob(
net::URLRequest* request,
@@ -419,7 +410,7 @@ class URLRequestJobWithCookies : public TestURLRequestJob {
URLRequestJobWithCookies(net::URLRequest* request,
net::NetworkDelegate* network_delegate,
net::CookieList* sent_cookies);
- ~URLRequestJobWithCookies() override {}
+ ~URLRequestJobWithCookies() override = default;
// net::URLRequestJob implementation:
void Start() override;
@@ -457,8 +448,9 @@ void URLRequestJobWithCookies::Start() {
options.set_include_httponly();
// See net::URLRequestHttpJob::AddCookieHeaderAndStart().
- url::Origin requested_origin(request_->url());
- url::Origin site_for_cookies(request_->site_for_cookies());
+ url::Origin requested_origin = url::Origin::Create(request_->url());
+ url::Origin site_for_cookies =
+ url::Origin::Create(request_->site_for_cookies());
if (net::registry_controlled_domains::SameDomainOrHost(
requested_origin, site_for_cookies,
@@ -682,7 +674,7 @@ class CrashReporterTest : public HeadlessBrowserTest,
inspector::ExperimentalObserver {
public:
CrashReporterTest() : devtools_client_(HeadlessDevToolsClient::Create()) {}
- ~CrashReporterTest() override {}
+ ~CrashReporterTest() override = default;
void SetUp() override {
base::ThreadRestrictions::SetIOAllowed(true);
@@ -792,7 +784,7 @@ IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, PermissionManagerAlwaysASK) {
class HeadlessBrowserTestWithNetLog : public HeadlessBrowserTest {
public:
- HeadlessBrowserTestWithNetLog() {}
+ HeadlessBrowserTestWithNetLog() = default;
void SetUp() override {
base::ThreadRestrictions::SetIOAllowed(true);
@@ -938,4 +930,45 @@ IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, AllowInsecureLocalhostFlag) {
EXPECT_TRUE(WaitForLoad(web_contents));
}
+class HeadlessBrowserTestAppendCommandLineFlags : public HeadlessBrowserTest {
+ public:
+ HeadlessBrowserTestAppendCommandLineFlags() {
+ options()->append_command_line_flags_callback = base::Bind(
+ &HeadlessBrowserTestAppendCommandLineFlags::AppendCommandLineFlags,
+ base::Unretained(this));
+ }
+
+ void AppendCommandLineFlags(base::CommandLine* command_line,
+ HeadlessBrowserContext* child_browser_context,
+ const std::string& child_process_type,
+ int child_process_id) {
+ if (child_process_type != "renderer")
+ return;
+
+ callback_was_run_ = true;
+ EXPECT_LE(0, child_process_id);
+ EXPECT_NE(nullptr, command_line);
+ EXPECT_NE(nullptr, child_browser_context);
+ }
+
+ protected:
+ bool callback_was_run_ = false;
+};
+
+IN_PROC_BROWSER_TEST_F(HeadlessBrowserTestAppendCommandLineFlags,
+ AppendChildProcessCommandLineFlags) {
+ // Create a new renderer process, and verify that callback was executed.
+ HeadlessBrowserContext* browser_context =
+ browser()->CreateBrowserContextBuilder().Build();
+ HeadlessWebContents* web_contents =
+ browser_context->CreateWebContentsBuilder()
+ .SetInitialURL(GURL("about:blank"))
+ .Build();
+
+ EXPECT_TRUE(callback_was_run_);
+
+ // Used only for lifetime.
+ (void)web_contents;
+}
+
} // namespace headless
diff --git a/chromium/headless/lib/headless_browser_context_browsertest.cc b/chromium/headless/lib/headless_browser_context_browsertest.cc
index 0e48d4cd052..a40b10a78f6 100644
--- a/chromium/headless/lib/headless_browser_context_browsertest.cc
+++ b/chromium/headless/lib/headless_browser_context_browsertest.cc
@@ -100,9 +100,7 @@ class HeadlessBrowserContextIsolationTest
}
void OnFirstSetCookieResult(std::unique_ptr<runtime::EvaluateResult> result) {
- std::string cookie;
- EXPECT_TRUE(result->GetResult()->GetValue()->GetAsString(&cookie));
- EXPECT_EQ(kMainPageCookie, cookie);
+ EXPECT_EQ(kMainPageCookie, result->GetResult()->GetValue()->GetString());
devtools_client2_->GetRuntime()->Evaluate(
base::StringPrintf("document.cookie = '%s'", kIsolatedPageCookie),
@@ -113,9 +111,8 @@ class HeadlessBrowserContextIsolationTest
void OnSecondSetCookieResult(
std::unique_ptr<runtime::EvaluateResult> result) {
- std::string cookie;
- EXPECT_TRUE(result->GetResult()->GetValue()->GetAsString(&cookie));
- EXPECT_EQ(kIsolatedPageCookie, cookie);
+ EXPECT_EQ(kIsolatedPageCookie,
+ result->GetResult()->GetValue()->GetString());
devtools_client_->GetRuntime()->Evaluate(
"document.cookie",
@@ -124,9 +121,7 @@ class HeadlessBrowserContextIsolationTest
}
void OnFirstGetCookieResult(std::unique_ptr<runtime::EvaluateResult> result) {
- std::string cookie;
- EXPECT_TRUE(result->GetResult()->GetValue()->GetAsString(&cookie));
- EXPECT_EQ(kMainPageCookie, cookie);
+ EXPECT_EQ(kMainPageCookie, result->GetResult()->GetValue()->GetString());
devtools_client2_->GetRuntime()->Evaluate(
"document.cookie",
@@ -137,9 +132,8 @@ class HeadlessBrowserContextIsolationTest
void OnSecondGetCookieResult(
std::unique_ptr<runtime::EvaluateResult> result) {
- std::string cookie;
- EXPECT_TRUE(result->GetResult()->GetValue()->GetAsString(&cookie));
- EXPECT_EQ(kIsolatedPageCookie, cookie);
+ EXPECT_EQ(kIsolatedPageCookie,
+ result->GetResult()->GetValue()->GetString());
FinishTest();
}
@@ -178,12 +172,11 @@ IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, ContextProtocolHandler) {
.Build();
EXPECT_TRUE(WaitForLoad(web_contents));
- std::string inner_html;
- EXPECT_TRUE(EvaluateScript(web_contents, "document.body.innerHTML")
- ->GetResult()
- ->GetValue()
- ->GetAsString(&inner_html));
- EXPECT_EQ(kResponseBody, inner_html);
+ EXPECT_EQ(kResponseBody,
+ EvaluateScript(web_contents, "document.body.innerHTML")
+ ->GetResult()
+ ->GetValue()
+ ->GetString());
web_contents->Close();
HeadlessBrowserContext* another_browser_context =
@@ -197,11 +190,10 @@ IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, ContextProtocolHandler) {
.SetInitialURL(GURL("http://not-an-actual-domain.tld/hello.html"))
.Build();
EXPECT_FALSE(WaitForLoad(web_contents));
- EXPECT_TRUE(EvaluateScript(web_contents, "document.body.innerHTML")
- ->GetResult()
- ->GetValue()
- ->GetAsString(&inner_html));
- EXPECT_EQ("", inner_html);
+ EXPECT_EQ("", EvaluateScript(web_contents, "document.body.innerHTML")
+ ->GetResult()
+ ->GetValue()
+ ->GetString());
web_contents->Close();
}
diff --git a/chromium/headless/lib/headless_content_client.cc b/chromium/headless/lib/headless_content_client.cc
index fa06d5a176e..8de358fec93 100644
--- a/chromium/headless/lib/headless_content_client.cc
+++ b/chromium/headless/lib/headless_content_client.cc
@@ -12,7 +12,7 @@ namespace headless {
HeadlessContentClient::HeadlessContentClient(HeadlessBrowser::Options* options)
: options_(options) {}
-HeadlessContentClient::~HeadlessContentClient() {}
+HeadlessContentClient::~HeadlessContentClient() = default;
std::string HeadlessContentClient::GetProduct() const {
return options_->product_name_and_version;
diff --git a/chromium/headless/lib/headless_content_main_delegate.cc b/chromium/headless/lib/headless_content_main_delegate.cc
index d08c3f646bd..30e19faeec4 100644
--- a/chromium/headless/lib/headless_content_main_delegate.cc
+++ b/chromium/headless/lib/headless_content_main_delegate.cc
@@ -17,6 +17,7 @@
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "components/crash/content/app/breakpad_linux.h"
+#include "components/crash/core/common/crash_key.h"
#include "content/public/browser/browser_main_runner.h"
#include "content/public/common/content_switches.h"
#include "headless/lib/browser/headless_browser_impl.h"
@@ -85,6 +86,9 @@ bool HeadlessContentMainDelegate::BasicStartupComplete(int* exit_code) {
if (browser_->options()->disable_sandbox)
command_line->AppendSwitch(switches::kNoSandbox);
+ if (!browser_->options()->enable_resource_scheduler)
+ command_line->AppendSwitch(switches::kDisableResourceScheduler);
+
#if defined(USE_OZONE)
// The headless backend is automatically chosen for a headless build, but also
// adding it here allows us to run in a non-headless build too.
@@ -183,6 +187,8 @@ void HeadlessContentMainDelegate::InitCrashReporter(
g_headless_crash_client.Pointer()->set_crash_dumps_dir(
browser_->options()->crash_dumps_dir);
+ crash_reporter::InitializeCrashKeys();
+
#if defined(HEADLESS_USE_BREAKPAD)
if (!browser_->options()->enable_crash_reporter) {
DCHECK(!breakpad::IsCrashReporterEnabled());
@@ -227,7 +233,8 @@ int HeadlessContentMainDelegate::RunProcess(
if (!process_type.empty())
return -1;
- base::trace_event::TraceLog::GetInstance()->SetProcessName("HeadlessBrowser");
+ base::trace_event::TraceLog::GetInstance()->set_process_name(
+ "HeadlessBrowser");
base::trace_event::TraceLog::GetInstance()->SetProcessSortIndex(
kTraceEventBrowserProcessSortIndex);
@@ -248,7 +255,7 @@ int HeadlessContentMainDelegate::RunProcess(
}
#endif // !defined(CHROME_MULTIPLE_DLL_CHILD)
-#if !defined(OS_MACOSX) && defined(OS_POSIX) && !defined(OS_ANDROID)
+#if defined(OS_LINUX)
void HeadlessContentMainDelegate::ZygoteForked() {
const base::CommandLine& command_line(
*base::CommandLine::ForCurrentProcess());
@@ -261,7 +268,7 @@ void HeadlessContentMainDelegate::ZygoteForked() {
breakpad::InitCrashReporter(process_type);
#endif
}
-#endif
+#endif // defined(OS_LINUX)
// static
HeadlessContentMainDelegate* HeadlessContentMainDelegate::GetInstance() {
diff --git a/chromium/headless/lib/headless_content_main_delegate.h b/chromium/headless/lib/headless_content_main_delegate.h
index 37d935177fc..91343b586ba 100644
--- a/chromium/headless/lib/headless_content_main_delegate.h
+++ b/chromium/headless/lib/headless_content_main_delegate.h
@@ -46,7 +46,7 @@ class HEADLESS_EXPORT HeadlessContentMainDelegate
HeadlessBrowserImpl* browser() const { return browser_.get(); }
-#if !defined(OS_MACOSX) && defined(OS_POSIX) && !defined(OS_ANDROID)
+#if defined(OS_LINUX)
void ZygoteForked() override;
#endif
diff --git a/chromium/headless/lib/headless_devtools_client_browsertest.cc b/chromium/headless/lib/headless_devtools_client_browsertest.cc
index 8f09e03741c..44d8061bea6 100644
--- a/chromium/headless/lib/headless_devtools_client_browsertest.cc
+++ b/chromium/headless/lib/headless_devtools_client_browsertest.cc
@@ -8,7 +8,6 @@
#include "base/files/file_util.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
-#include "base/message_loop/message_loop.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/strings/string_util.h"
@@ -44,6 +43,7 @@
} while (false)
using testing::ElementsAre;
+using testing::NotNull;
namespace headless {
@@ -75,9 +75,7 @@ class HeadlessDevToolsClientNavigationTest
.SetUrl(embedded_test_server()->GetURL("/hello.html").spec())
.Build();
devtools_client_->GetPage()->GetExperimental()->AddObserver(this);
- base::RunLoop run_loop;
- base::MessageLoop::ScopedNestableTaskAllower nest_loop(
- base::MessageLoop::current());
+ base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
devtools_client_->GetPage()->Enable(run_loop.QuitClosure());
run_loop.Run();
devtools_client_->GetPage()->Navigate(std::move(params));
@@ -275,17 +273,13 @@ class HeadlessDevToolsClientEvalTest
}
void OnFirstResult(std::unique_ptr<runtime::EvaluateResult> result) {
- int value;
EXPECT_TRUE(result->GetResult()->HasValue());
- EXPECT_TRUE(result->GetResult()->GetValue()->GetAsInteger(&value));
- EXPECT_EQ(3, value);
+ EXPECT_EQ(3, result->GetResult()->GetValue()->GetInt());
}
void OnSecondResult(std::unique_ptr<runtime::EvaluateResult> result) {
- int value;
EXPECT_TRUE(result->GetResult()->HasValue());
- EXPECT_TRUE(result->GetResult()->GetValue()->GetAsInteger(&value));
- EXPECT_EQ(168, value);
+ EXPECT_EQ(168, result->GetResult()->GetValue()->GetInt());
FinishAsynchronousTest();
}
};
@@ -334,11 +328,9 @@ class HeadlessDevToolsClientObserverTest
public:
void RunDevTooledTest() override {
EXPECT_TRUE(embedded_test_server()->Start());
- base::RunLoop run_loop;
+ base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
devtools_client_->GetNetwork()->AddObserver(this);
devtools_client_->GetNetwork()->Enable(run_loop.QuitClosure());
- base::MessageLoop::ScopedNestableTaskAllower nest_loop(
- base::MessageLoop::current());
run_loop.Run();
devtools_client_->GetPage()->Navigate(
@@ -356,10 +348,10 @@ class HeadlessDevToolsClientObserverTest
const network::ResponseReceivedParams& params) override {
EXPECT_EQ(200, params.GetResponse()->GetStatus());
EXPECT_EQ("OK", params.GetResponse()->GetStatusText());
- std::string content_type;
- EXPECT_TRUE(params.GetResponse()->GetHeaders()->GetString("Content-Type",
- &content_type));
- EXPECT_EQ("text/html", content_type);
+ const base::Value* content_type_value =
+ params.GetResponse()->GetHeaders()->FindKey("Content-Type");
+ ASSERT_THAT(content_type_value, NotNull());
+ EXPECT_EQ("text/html", content_type_value->GetString());
devtools_client_->GetNetwork()->Disable();
devtools_client_->GetNetwork()->RemoveObserver(this);
@@ -375,11 +367,9 @@ class HeadlessDevToolsClientExperimentalTest
public:
void RunDevTooledTest() override {
EXPECT_TRUE(embedded_test_server()->Start());
- base::RunLoop run_loop;
+ base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
devtools_client_->GetPage()->GetExperimental()->AddObserver(this);
devtools_client_->GetPage()->Enable(run_loop.QuitClosure());
- base::MessageLoop::ScopedNestableTaskAllower nest_loop(
- base::MessageLoop::current());
run_loop.Run();
// Check that experimental commands require parameter objects.
devtools_client_->GetRuntime()
@@ -612,11 +602,9 @@ class TargetDomainCreateTwoContexts : public HeadlessAsyncDevTooledBrowserTest,
void RunDevTooledTest() override {
EXPECT_TRUE(embedded_test_server()->Start());
- base::RunLoop run_loop;
+ base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
devtools_client_->GetPage()->AddObserver(this);
devtools_client_->GetPage()->Enable(run_loop.QuitClosure());
- base::MessageLoop::ScopedNestableTaskAllower nest_loop(
- base::MessageLoop::current());
run_loop.Run();
devtools_client_->GetTarget()->GetExperimental()->AddObserver(this);
@@ -758,9 +746,8 @@ class TargetDomainCreateTwoContexts : public HeadlessAsyncDevTooledBrowserTest,
return;
}
- std::string method;
- if (message_dict->GetString("method", &method) &&
- method == "Page.loadEventFired") {
+ const base::Value* method_value = message_dict->FindKey("method");
+ if (method_value && method_value->GetString() == "Page.loadEventFired") {
if (params.GetTargetId() == page_id_one_) {
page_one_loaded_ = true;
} else if (params.GetTargetId() == page_id_two_) {
@@ -770,9 +757,10 @@ class TargetDomainCreateTwoContexts : public HeadlessAsyncDevTooledBrowserTest,
return;
}
- int message_id = 0;
- if (!message_dict->GetInteger("id", &message_id))
+ const base::Value* id_value = message_dict->FindKey("id");
+ if (!id_value)
return;
+ int message_id = id_value->GetInt();
const base::DictionaryValue* result_dict;
if (message_dict->GetDictionary("result", &result_dict)) {
if (message_id == 101) {
@@ -811,9 +799,10 @@ class TargetDomainCreateTwoContexts : public HeadlessAsyncDevTooledBrowserTest,
// There's a nested result. We want the inner one.
EXPECT_TRUE(result_dict->GetDictionary("result", &result_dict));
- std::string value;
- EXPECT_TRUE(result_dict->GetString("value", &value));
- EXPECT_EQ("", value) << "Page 2 should not share cookies from page one";
+ const base::Value* value_value = result_dict->FindKey("value");
+ ASSERT_THAT(value_value, NotNull());
+ EXPECT_EQ("", value_value->GetString())
+ << "Page 2 should not share cookies from page one";
devtools_client_->GetTarget()->GetExperimental()->CloseTarget(
target::CloseTargetParams::Builder()
@@ -884,20 +873,21 @@ class HeadlessDevToolsNavigationControlTest
public:
void RunDevTooledTest() override {
EXPECT_TRUE(embedded_test_server()->Start());
- base::RunLoop run_loop;
+ base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
devtools_client_->GetPage()->GetExperimental()->AddObserver(this);
devtools_client_->GetNetwork()->GetExperimental()->AddObserver(this);
devtools_client_->GetPage()->Enable(run_loop.QuitClosure());
- base::MessageLoop::ScopedNestableTaskAllower nest_loop(
- base::MessageLoop::current());
run_loop.Run();
devtools_client_->GetNetwork()->Enable();
- devtools_client_->GetNetwork()
- ->GetExperimental()
- ->SetRequestInterceptionEnabled(
- network::SetRequestInterceptionEnabledParams::Builder()
- .SetEnabled(true)
- .Build());
+
+ std::unique_ptr<headless::network::RequestPattern> match_all =
+ headless::network::RequestPattern::Builder().SetUrlPattern("*").Build();
+ std::vector<std::unique_ptr<headless::network::RequestPattern>> patterns;
+ patterns.push_back(std::move(match_all));
+ devtools_client_->GetNetwork()->GetExperimental()->SetRequestInterception(
+ network::SetRequestInterceptionParams::Builder()
+ .SetPatterns(std::move(patterns))
+ .Build());
devtools_client_->GetPage()->Navigate(
embedded_test_server()->GetURL("/hello.html").spec());
}
@@ -980,10 +970,8 @@ class HeadlessDevToolsClientAttachTest
}
void OnFirstResult(std::unique_ptr<runtime::EvaluateResult> result) {
- int value;
EXPECT_TRUE(result->GetResult()->HasValue());
- EXPECT_TRUE(result->GetResult()->GetValue()->GetAsInteger(&value));
- EXPECT_EQ(24 * 7, value);
+ EXPECT_EQ(24 * 7, result->GetResult()->GetValue()->GetInt());
HeadlessDevToolsTarget* devtools_target =
web_contents_->GetDevToolsTarget();
@@ -999,10 +987,8 @@ class HeadlessDevToolsClientAttachTest
}
void OnSecondResult(std::unique_ptr<runtime::EvaluateResult> result) {
- int value;
EXPECT_TRUE(result->GetResult()->HasValue());
- EXPECT_TRUE(result->GetResult()->GetValue()->GetAsInteger(&value));
- EXPECT_EQ(27 * 4, value);
+ EXPECT_EQ(27 * 4, result->GetResult()->GetValue()->GetInt());
// If everything worked, this call will not crash, since it
// detaches devtools_client_.
@@ -1021,11 +1007,9 @@ class HeadlessDevToolsMethodCallErrorTest
public:
void RunDevTooledTest() override {
EXPECT_TRUE(embedded_test_server()->Start());
- base::RunLoop run_loop;
+ base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
devtools_client_->GetPage()->AddObserver(this);
devtools_client_->GetPage()->Enable(run_loop.QuitClosure());
- base::MessageLoop::ScopedNestableTaskAllower nest_loop(
- base::MessageLoop::current());
run_loop.Run();
devtools_client_->GetPage()->Navigate(
embedded_test_server()->GetURL("/hello.html").spec());
@@ -1063,13 +1047,11 @@ class HeadlessDevToolsNetworkBlockedUrlTest
public:
void RunDevTooledTest() override {
EXPECT_TRUE(embedded_test_server()->Start());
- base::RunLoop run_loop;
+ base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
devtools_client_->GetPage()->AddObserver(this);
devtools_client_->GetPage()->Enable();
devtools_client_->GetNetwork()->AddObserver(this);
devtools_client_->GetNetwork()->Enable(run_loop.QuitClosure());
- base::MessageLoop::ScopedNestableTaskAllower nest_loop(
- base::MessageLoop::current());
run_loop.Run();
std::vector<std::string> blockedUrls;
blockedUrls.push_back("dom_tree_test.css");
@@ -1131,14 +1113,12 @@ class DevToolsHeaderStrippingTest : public HeadlessAsyncDevTooledBrowserTest,
public network::Observer {
void RunDevTooledTest() override {
EXPECT_TRUE(embedded_test_server()->Start());
- base::RunLoop run_loop;
+ base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
devtools_client_->GetPage()->AddObserver(this);
devtools_client_->GetPage()->Enable();
// Enable network domain in order to get DevTools to add the header.
devtools_client_->GetNetwork()->AddObserver(this);
devtools_client_->GetNetwork()->Enable(run_loop.QuitClosure());
- base::MessageLoop::ScopedNestableTaskAllower nest_loop(
- base::MessageLoop::current());
run_loop.Run();
devtools_client_->GetPage()->Navigate(
"http://not-an-actual-domain.tld/hello.html");
@@ -1166,6 +1146,38 @@ class DevToolsHeaderStrippingTest : public HeadlessAsyncDevTooledBrowserTest,
HEADLESS_ASYNC_DEVTOOLED_TEST_F(DevToolsHeaderStrippingTest);
+class DevToolsNetworkOfflineEmulationTest
+ : public HeadlessAsyncDevTooledBrowserTest,
+ public page::Observer,
+ public network::Observer {
+ void RunDevTooledTest() override {
+ EXPECT_TRUE(embedded_test_server()->Start());
+ base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
+ devtools_client_->GetPage()->AddObserver(this);
+ devtools_client_->GetPage()->Enable();
+ devtools_client_->GetNetwork()->AddObserver(this);
+ devtools_client_->GetNetwork()->Enable(run_loop.QuitClosure());
+ run_loop.Run();
+ std::unique_ptr<network::EmulateNetworkConditionsParams> params =
+ network::EmulateNetworkConditionsParams::Builder()
+ .SetOffline(true)
+ .SetLatency(0)
+ .SetDownloadThroughput(0)
+ .SetUploadThroughput(0)
+ .Build();
+ devtools_client_->GetNetwork()->EmulateNetworkConditions(std::move(params));
+ devtools_client_->GetPage()->Navigate(
+ embedded_test_server()->GetURL("/hello.html").spec());
+ }
+
+ void OnLoadingFailed(const network::LoadingFailedParams& failed) override {
+ EXPECT_EQ("net::ERR_INTERNET_DISCONNECTED", failed.GetErrorText());
+ FinishAsynchronousTest();
+ }
+};
+
+HEADLESS_ASYNC_DEVTOOLED_TEST_F(DevToolsNetworkOfflineEmulationTest);
+
class RawDevtoolsProtocolTest
: public HeadlessAsyncDevTooledBrowserTest,
public HeadlessDevToolsClient::RawProtocolListener {
@@ -1263,24 +1275,27 @@ class DomTreeExtractionBrowserTest : public HeadlessAsyncDevTooledBrowserTest,
base::DictionaryValue* node_dict = dom_nodes[i].get();
// Frame IDs are random.
- if (node_dict->HasKey("frameId"))
+ if (node_dict->FindKey("frameId"))
node_dict->SetString("frameId", "?");
// Ports are random.
- std::string url;
- if (node_dict->GetString("baseURL", &url)) {
- node_dict->SetString("baseURL",
- GURL(url).ReplaceComponents(replace_port).spec());
+ if (base::Value* base_url_value = node_dict->FindKey("baseURL")) {
+ node_dict->SetString("baseURL", GURL(base_url_value->GetString())
+ .ReplaceComponents(replace_port)
+ .spec());
}
- if (node_dict->GetString("documentURL", &url)) {
+ if (base::Value* document_url_value = node_dict->FindKey("documentURL")) {
node_dict->SetString("documentURL",
- GURL(url).ReplaceComponents(replace_port).spec());
+ GURL(document_url_value->GetString())
+ .ReplaceComponents(replace_port)
+ .spec());
}
// Merge LayoutTreeNode data into the dictionary.
- int layout_node_index;
- if (node_dict->GetInteger("layoutNodeIndex", &layout_node_index)) {
+ if (base::Value* layout_node_index_value =
+ node_dict->FindKey("layoutNodeIndex")) {
+ int layout_node_index = layout_node_index_value->GetInt();
ASSERT_LE(0, layout_node_index);
ASSERT_GT(result->GetLayoutTreeNodes()->size(),
static_cast<size_t>(layout_node_index));
@@ -1299,8 +1314,8 @@ class DomTreeExtractionBrowserTest : public HeadlessAsyncDevTooledBrowserTest,
if (layout_node->HasInlineTextNodes()) {
std::unique_ptr<base::ListValue> inline_text_nodes(
new base::ListValue());
- for (const std::unique_ptr<css::InlineTextBox>& inline_text_box :
- *layout_node->GetInlineTextNodes()) {
+ for (const std::unique_ptr<dom_snapshot::InlineTextBox>&
+ inline_text_box : *layout_node->GetInlineTextNodes()) {
size_t index = inline_text_nodes->GetSize();
inline_text_nodes->Set(index, inline_text_box->Serialize());
}
@@ -1384,21 +1399,22 @@ class UrlRequestFailedTest : public HeadlessAsyncDevTooledBrowserTest,
EXPECT_TRUE(embedded_test_server()->Start());
devtools_client_->GetNetwork()->GetExperimental()->AddObserver(this);
devtools_client_->GetNetwork()->Enable();
- devtools_client_->GetNetwork()
- ->GetExperimental()
- ->SetRequestInterceptionEnabled(
- network::SetRequestInterceptionEnabledParams::Builder()
- .SetEnabled(true)
- .Build());
+
+ std::unique_ptr<headless::network::RequestPattern> match_all =
+ headless::network::RequestPattern::Builder().SetUrlPattern("*").Build();
+ std::vector<std::unique_ptr<headless::network::RequestPattern>> patterns;
+ patterns.push_back(std::move(match_all));
+ devtools_client_->GetNetwork()->GetExperimental()->SetRequestInterception(
+ network::SetRequestInterceptionParams::Builder()
+ .SetPatterns(std::move(patterns))
+ .Build());
browser_context_->AddObserver(this);
devtools_client_->GetPage()->AddObserver(this);
- base::RunLoop run_loop;
+ base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
devtools_client_->GetPage()->Enable(run_loop.QuitClosure());
- base::MessageLoop::ScopedNestableTaskAllower nest_loop(
- base::MessageLoop::current());
run_loop.Run();
devtools_client_->GetPage()->Navigate(
@@ -1484,10 +1500,8 @@ class DevToolsSetCookieTest : public HeadlessAsyncDevTooledBrowserTest,
EXPECT_TRUE(embedded_test_server()->Start());
devtools_client_->GetNetwork()->AddObserver(this);
- base::RunLoop run_loop;
+ base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
devtools_client_->GetNetwork()->Enable(run_loop.QuitClosure());
- base::MessageLoop::ScopedNestableTaskAllower nest_loop(
- base::MessageLoop::current());
run_loop.Run();
devtools_client_->GetPage()->Navigate(
@@ -1523,19 +1537,19 @@ class DevtoolsInterceptionWithAuthProxyTest
EXPECT_TRUE(embedded_test_server()->Start());
devtools_client_->GetNetwork()->GetExperimental()->AddObserver(this);
devtools_client_->GetNetwork()->Enable();
- devtools_client_->GetNetwork()
- ->GetExperimental()
- ->SetRequestInterceptionEnabled(
- network::SetRequestInterceptionEnabledParams::Builder()
- .SetEnabled(true)
- .Build());
+ std::unique_ptr<headless::network::RequestPattern> match_all =
+ headless::network::RequestPattern::Builder().SetUrlPattern("*").Build();
+ std::vector<std::unique_ptr<headless::network::RequestPattern>> patterns;
+ patterns.push_back(std::move(match_all));
+ devtools_client_->GetNetwork()->GetExperimental()->SetRequestInterception(
+ network::SetRequestInterceptionParams::Builder()
+ .SetPatterns(std::move(patterns))
+ .Build());
devtools_client_->GetPage()->AddObserver(this);
- base::RunLoop run_loop;
+ base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
devtools_client_->GetPage()->Enable(run_loop.QuitClosure());
- base::MessageLoop::ScopedNestableTaskAllower nest_loop(
- base::MessageLoop::current());
run_loop.Run();
devtools_client_->GetPage()->Navigate(
@@ -1604,10 +1618,9 @@ class NavigatorLanguages : public HeadlessAsyncDevTooledBrowserTest {
}
void OnResult(std::unique_ptr<runtime::EvaluateResult> result) {
- std::string value;
EXPECT_TRUE(result->GetResult()->HasValue());
- EXPECT_TRUE(result->GetResult()->GetValue()->GetAsString(&value));
- EXPECT_EQ("[\"en-UK\",\"DE\",\"FR\"]", value);
+ EXPECT_EQ("[\"en-UK\",\"DE\",\"FR\"]",
+ result->GetResult()->GetValue()->GetString());
FinishAsynchronousTest();
}
diff --git a/chromium/headless/lib/headless_web_contents_browsertest.cc b/chromium/headless/lib/headless_web_contents_browsertest.cc
index 34270082532..17b264ec842 100644
--- a/chromium/headless/lib/headless_web_contents_browsertest.cc
+++ b/chromium/headless/lib/headless_web_contents_browsertest.cc
@@ -9,12 +9,12 @@
#include "base/base64.h"
#include "base/command_line.h"
#include "base/json/json_writer.h"
-#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "cc/base/switches.h"
#include "content/public/browser/web_contents.h"
+#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test.h"
#include "headless/lib/browser/headless_web_contents_impl.h"
#include "headless/public/devtools/domains/dom_snapshot.h"
@@ -66,8 +66,8 @@ class MockHeadlessBrowserContextObserver
MOCK_METHOD2(OnChildContentsCreated,
void(HeadlessWebContents*, HeadlessWebContents*));
- MockHeadlessBrowserContextObserver() {}
- virtual ~MockHeadlessBrowserContextObserver() {}
+ MockHeadlessBrowserContextObserver() = default;
+ virtual ~MockHeadlessBrowserContextObserver() = default;
HeadlessWebContents* last_parent;
HeadlessWebContents* last_child;
@@ -186,21 +186,23 @@ class HeadlessWindowOpenTabSocketTest : public HeadlessBrowserTest,
// runtime::Observer implementation.
void OnExecutionContextCreated(
const runtime::ExecutionContextCreatedParams& params) override {
- const base::DictionaryValue* dictionary;
std::string frame_id;
- if (params.GetContext()->HasAuxData() &&
- params.GetContext()->GetAuxData()->GetAsDictionary(&dictionary) &&
- dictionary->GetString("frameId", &frame_id) &&
- frame_id == *child_frame_id_) {
- child_frame_execution_context_id_ = params.GetContext()->GetId();
-
- HeadlessTabSocket* tab_socket = child_->GetHeadlessTabSocket();
- CHECK(tab_socket);
- tab_socket->InstallHeadlessTabSocketBindings(
- *child_frame_execution_context_id_,
- base::Bind(&HeadlessWindowOpenTabSocketTest::OnTabSocketInstalled,
- base::Unretained(this)));
- }
+ if (!params.GetContext()->HasAuxData())
+ return;
+
+ const base::Value* frame_id_value =
+ params.GetContext()->GetAuxData()->FindKey("frameId");
+ if (!frame_id_value || frame_id_value->GetString() != *child_frame_id_)
+ return;
+
+ child_frame_execution_context_id_ = params.GetContext()->GetId();
+
+ HeadlessTabSocket* tab_socket = child_->GetHeadlessTabSocket();
+ CHECK(tab_socket);
+ tab_socket->InstallHeadlessTabSocketBindings(
+ *child_frame_execution_context_id_,
+ base::Bind(&HeadlessWindowOpenTabSocketTest::OnTabSocketInstalled,
+ base::Unretained(this)));
}
void OnTabSocketInstalled(bool success) {
@@ -259,7 +261,7 @@ IN_PROC_BROWSER_TEST_F(HeadlessWindowOpenTabSocketTest,
class HeadlessNoDevToolsTabSocketTest : public HeadlessBrowserTest,
public HeadlessTabSocket::Listener {
public:
- HeadlessNoDevToolsTabSocketTest() {}
+ HeadlessNoDevToolsTabSocketTest() = default;
void SetUp() override {
options()->mojo_service_names.insert("headless::TabSocket");
@@ -328,12 +330,9 @@ IN_PROC_BROWSER_TEST_F(HeadlessWebContentsTest, Focus) {
.Build();
EXPECT_TRUE(WaitForLoad(web_contents));
- bool result;
- EXPECT_TRUE(EvaluateScript(web_contents, "document.hasFocus()")
- ->GetResult()
- ->GetValue()
- ->GetAsBoolean(&result));
- EXPECT_TRUE(result);
+ std::unique_ptr<runtime::EvaluateResult> has_focus =
+ EvaluateScript(web_contents, "document.hasFocus()");
+ EXPECT_TRUE(has_focus->GetResult()->GetValue()->GetBool());
HeadlessWebContents* web_contents2 =
browser_context->CreateWebContentsBuilder()
@@ -342,16 +341,11 @@ IN_PROC_BROWSER_TEST_F(HeadlessWebContentsTest, Focus) {
EXPECT_TRUE(WaitForLoad(web_contents2));
// Focus of different WebContents is independent.
- EXPECT_TRUE(EvaluateScript(web_contents, "document.hasFocus()")
- ->GetResult()
- ->GetValue()
- ->GetAsBoolean(&result));
- EXPECT_TRUE(result);
- EXPECT_TRUE(EvaluateScript(web_contents2, "document.hasFocus()")
- ->GetResult()
- ->GetValue()
- ->GetAsBoolean(&result));
- EXPECT_TRUE(result);
+ has_focus = EvaluateScript(web_contents, "document.hasFocus()");
+ EXPECT_TRUE(has_focus->GetResult()->GetValue()->GetBool());
+
+ has_focus = EvaluateScript(web_contents2, "document.hasFocus()");
+ EXPECT_TRUE(has_focus->GetResult()->GetValue()->GetBool());
}
IN_PROC_BROWSER_TEST_F(HeadlessWebContentsTest, HandleSSLError) {
@@ -449,7 +443,7 @@ class HeadlessWebContentsPDFTest : public HeadlessAsyncDevTooledBrowserTest {
void RunDevTooledTest() override {
std::string height_expression = "document.body.style.height = '" +
- base::DoubleToString(kDocHeight) + "in'";
+ base::NumberToString(kDocHeight) + "in'";
std::unique_ptr<runtime::EvaluateParams> params =
runtime::EvaluateParams::Builder()
.SetExpression("document.body.style.background = '#123456';" +
@@ -855,9 +849,7 @@ class HeadlessWebContentsRequestStorageQuotaTest
void RunDevTooledTest() override {
EXPECT_TRUE(embedded_test_server()->Start());
- base::RunLoop run_loop;
- base::MessageLoop::ScopedNestableTaskAllower nest_loop(
- base::MessageLoop::current());
+ base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
devtools_client_->GetRuntime()->AddObserver(this);
devtools_client_->GetRuntime()->Enable(run_loop.QuitClosure());
run_loop.Run();
@@ -927,37 +919,35 @@ namespace {
const char* kRequestOrderTestPage = R"(
<html>
<body>
- <img src="img0">
- <img src="img1">
- <img src="img2">
- <img src="img3">
- <img src="img4">
- <img src="img5">
- <img src="img6">
+ <script src='script1' async></script>
+ <script src='script2' async></script>
+ <script src='script3' async></script>
+ <script src='script4' async></script>
+ <script src='script5' async></script>
+ <script src='script6' async></script>
<script src='script7' async></script>
- <script>
- var xhr = new XMLHttpRequest();
- xhr.open('GET', 'xhr8');
- xhr.send();
- </script>
- <iframe src=frame9></iframe>
- <img src="img10">
- <img src="img11">
+ <script src='script8' async></script>
+ <script src='script9' async></script>
+ <script src='script10' async></script>
+ <script src='script11' async></script>
+ <script src='script12' async></script>
+ <script src='script13' async></script>
+ <script src='script14' async></script>
+ <script src='script15' async></script>
+ <script src='script16' async></script>
+ <script src='script17' async></script>
+ <script src='script18' async></script>
+ <script src='script19' async></script>
+ <script src='script20' async></script>
</body>
</html> )";
-const char* kRequestOrderTestPageUrls[] = {
- "http://foo.com/index.html", "http://foo.com/img0",
- "http://foo.com/img1", "http://foo.com/img2",
- "http://foo.com/img3", "http://foo.com/img4",
- "http://foo.com/img5", "http://foo.com/img6",
- "http://foo.com/script7", "http://foo.com/img10",
- "http://foo.com/img11", "http://foo.com/xhr8",
- "http://foo.com/frame9"};
} // namespace
-class ResourceSchedulerTest : public HeadlessAsyncDevTooledBrowserTest,
- public page::Observer {
+class ResourceSchedulerTest
+ : public HeadlessAsyncDevTooledBrowserTest,
+ public page::Observer,
+ public TestInMemoryProtocolHandler::RequestDeferrer {
public:
void SetUp() override {
options()->enable_resource_scheduler = GetEnableResourceScheduler();
@@ -968,10 +958,8 @@ class ResourceSchedulerTest : public HeadlessAsyncDevTooledBrowserTest,
http_handler_->SetHeadlessBrowserContext(browser_context_);
devtools_client_->GetPage()->AddObserver(this);
- base::RunLoop run_loop;
+ base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
devtools_client_->GetPage()->Enable(run_loop.QuitClosure());
- base::MessageLoop::ScopedNestableTaskAllower nest_loop(
- base::MessageLoop::current());
run_loop.Run();
devtools_client_->GetPage()->Navigate("http://foo.com/index.html");
@@ -982,8 +970,7 @@ class ResourceSchedulerTest : public HeadlessAsyncDevTooledBrowserTest,
ProtocolHandlerMap GetProtocolHandlers() override {
ProtocolHandlerMap protocol_handlers;
std::unique_ptr<TestInMemoryProtocolHandler> http_handler(
- new TestInMemoryProtocolHandler(browser()->BrowserIOThread(),
- /* simulate_slow_fetch */ true));
+ new TestInMemoryProtocolHandler(browser()->BrowserIOThread(), this));
http_handler_ = http_handler.get();
http_handler_->InsertResponse("http://foo.com/index.html",
{kRequestOrderTestPage, "text/html"});
@@ -991,27 +978,45 @@ class ResourceSchedulerTest : public HeadlessAsyncDevTooledBrowserTest,
return protocol_handlers;
}
+ void OnRequest(const GURL& url, base::Closure complete_request) override {
+ if (max_requests_in_flight_ < ++num_requests_in_flight_)
+ max_requests_in_flight_ = num_requests_in_flight_;
+ browser()->BrowserIOThread()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&ResourceSchedulerTest::AllowRequest, base::Unretained(this),
+ complete_request),
+ base::TimeDelta::FromMilliseconds(100));
+ }
+
+ void AllowRequest(base::Closure complete_request) {
+ num_requests_in_flight_--;
+ complete_request.Run();
+ }
+
const TestInMemoryProtocolHandler* http_handler() const {
return http_handler_;
}
+ int max_requests_in_flight() const { return max_requests_in_flight_; }
+
private:
TestInMemoryProtocolHandler* http_handler_; // NOT OWNED
+ int num_requests_in_flight_ = 0;
+ int max_requests_in_flight_ = 0;
};
-// TODO(alexclarke): Fix the flakes. http://crbug.com/766884
-class DISABLED_DisableResourceSchedulerTest : public ResourceSchedulerTest {
+class DisableResourceSchedulerTest : public ResourceSchedulerTest {
public:
bool GetEnableResourceScheduler() override { return false; }
void OnLoadEventFired(const page::LoadEventFiredParams&) override {
- EXPECT_THAT(http_handler()->urls_requested(),
- ElementsAreArray(kRequestOrderTestPageUrls));
+ // All scripts should have been requested simultaneously.
+ EXPECT_EQ(20, max_requests_in_flight());
FinishAsynchronousTest();
}
};
-HEADLESS_ASYNC_DEVTOOLED_TEST_F(DISABLED_DisableResourceSchedulerTest);
+HEADLESS_ASYNC_DEVTOOLED_TEST_F(DisableResourceSchedulerTest);
class EnableResourceSchedulerTest : public ResourceSchedulerTest {
public:
@@ -1020,12 +1025,8 @@ class EnableResourceSchedulerTest : public ResourceSchedulerTest {
}
void OnLoadEventFired(const page::LoadEventFiredParams&) override {
- // We expect a different resource order when the ResourceScheduler is used.
- EXPECT_THAT(http_handler()->urls_requested(),
- Not(ElementsAreArray(kRequestOrderTestPageUrls)));
- // However all the same urls should still be requested.
- EXPECT_THAT(http_handler()->urls_requested(),
- UnorderedElementsAreArray(kRequestOrderTestPageUrls));
+ // Only a limited number of scripts should be requested simultaneously.
+ EXPECT_EQ(6, max_requests_in_flight());
FinishAsynchronousTest();
}
};
@@ -1083,6 +1084,7 @@ class HeadlessWebContentsBeginFrameControlTest
void SetUpCommandLine(base::CommandLine* command_line) override {
HeadlessBrowserTest::SetUpCommandLine(command_line);
command_line->AppendSwitch(cc::switches::kRunAllCompositorStagesBeforeDraw);
+ command_line->AppendSwitch(switches::kDisableNewContentRenderingTimeout);
}
void OnCreateTargetResult(
@@ -1218,7 +1220,7 @@ class HeadlessWebContentsBeginFrameControlTest
class HeadlessWebContentsBeginFrameControlBasicTest
: public HeadlessWebContentsBeginFrameControlTest {
public:
- HeadlessWebContentsBeginFrameControlBasicTest() {}
+ HeadlessWebContentsBeginFrameControlBasicTest() = default;
protected:
std::string GetTestHtmlFile() override {
@@ -1231,9 +1233,8 @@ class HeadlessWebContentsBeginFrameControlBasicTest
void OnFrameFinished(std::unique_ptr<headless_experimental::BeginFrameResult>
result) override {
if (!sent_screenshot_request_) {
- // Once no more BeginFrames are needed and the main frame is ready,
- // capture a screenshot.
- sent_screenshot_request_ = !needs_begin_frames_ && main_frame_ready_;
+ // Once the main frame is ready, capture a screenshot.
+ sent_screenshot_request_ = main_frame_ready_;
BeginFrame(sent_screenshot_request_);
} else {
EXPECT_TRUE(result->GetHasDamage());
@@ -1262,11 +1263,10 @@ class HeadlessWebContentsBeginFrameControlBasicTest
HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessWebContentsBeginFrameControlBasicTest);
-// TODO(eseckler).
class HeadlessWebContentsBeginFrameControlViewportTest
: public HeadlessWebContentsBeginFrameControlTest {
public:
- HeadlessWebContentsBeginFrameControlViewportTest() {}
+ HeadlessWebContentsBeginFrameControlViewportTest() = default;
protected:
std::string GetTestHtmlFile() override {
@@ -1279,16 +1279,10 @@ class HeadlessWebContentsBeginFrameControlViewportTest
void OnFrameFinished(std::unique_ptr<headless_experimental::BeginFrameResult>
result) override {
if (!sent_screenshot_request_) {
- // Once no more BeginFrames are needed and the main frame is ready,
- // set the view size and position and then capture a screenshot.
- if (!needs_begin_frames_ && main_frame_ready_) {
- if (did_set_up_viewport_) {
- // Finally, capture a screenshot.
- sent_screenshot_request_ = true;
- BeginFrame(true);
- } else {
- SetUpViewport();
- }
+ // Once the main frame is ready, set the view size and position and then
+ // capture a screenshot.
+ if (main_frame_ready_) {
+ SetUpViewport();
return;
}
@@ -1325,8 +1319,6 @@ class HeadlessWebContentsBeginFrameControlViewportTest
}
void SetUpViewport() {
- did_set_up_viewport_ = 1;
- // We should be needing BeginFrames again because of the viewport change.
devtools_client_->GetEmulation()
->GetExperimental()
->SetDeviceMetricsOverride(
@@ -1342,11 +1334,21 @@ class HeadlessWebContentsBeginFrameControlViewportTest
.SetHeight(100)
.SetScale(2)
.Build())
- .Build());
+ .Build(),
+ base::Bind(&HeadlessWebContentsBeginFrameControlViewportTest::
+ SetDeviceMetricsOverrideDone,
+ base::Unretained(this)));
+ }
+
+ void SetDeviceMetricsOverrideDone(
+ std::unique_ptr<emulation::SetDeviceMetricsOverrideResult> result) {
+ EXPECT_TRUE(result);
+ // Take a screenshot.
+ sent_screenshot_request_ = true;
+ BeginFrame(true);
}
bool sent_screenshot_request_ = false;
- bool did_set_up_viewport_ = false;
};
HEADLESS_ASYNC_DEVTOOLED_TEST_F(
@@ -1354,4 +1356,74 @@ HEADLESS_ASYNC_DEVTOOLED_TEST_F(
#endif // !defined(OS_MACOSX)
+class CookiesEnabled : public HeadlessAsyncDevTooledBrowserTest,
+ page::Observer {
+ public:
+ void RunDevTooledTest() override {
+ devtools_client_->GetPage()->AddObserver(this);
+ devtools_client_->GetPage()->Enable();
+
+ EXPECT_TRUE(embedded_test_server()->Start());
+ devtools_client_->GetPage()->Navigate(
+ embedded_test_server()->GetURL("/cookie.html").spec());
+ }
+
+ // page::Observer implementation:
+ void OnLoadEventFired(const page::LoadEventFiredParams& params) override {
+ devtools_client_->GetRuntime()->Evaluate(
+ "window.test_result",
+ base::Bind(&CookiesEnabled::OnResult, base::Unretained(this)));
+ }
+
+ void OnResult(std::unique_ptr<runtime::EvaluateResult> result) {
+ std::string value;
+ EXPECT_TRUE(result->GetResult()->HasValue());
+ EXPECT_TRUE(result->GetResult()->GetValue()->GetAsString(&value));
+ EXPECT_EQ("0", value);
+ FinishAsynchronousTest();
+ }
+
+ void CustomizeHeadlessBrowserContext(
+ HeadlessBrowserContext::Builder& builder) override {
+ builder.SetAllowCookies(true);
+ }
+};
+
+HEADLESS_ASYNC_DEVTOOLED_TEST_F(CookiesEnabled);
+
+class CookiesDisabled : public HeadlessAsyncDevTooledBrowserTest,
+ page::Observer {
+ public:
+ void RunDevTooledTest() override {
+ devtools_client_->GetPage()->AddObserver(this);
+ devtools_client_->GetPage()->Enable();
+
+ EXPECT_TRUE(embedded_test_server()->Start());
+ devtools_client_->GetPage()->Navigate(
+ embedded_test_server()->GetURL("/cookie.html").spec());
+ }
+
+ // page::Observer implementation:
+ void OnLoadEventFired(const page::LoadEventFiredParams& params) override {
+ devtools_client_->GetRuntime()->Evaluate(
+ "window.test_result",
+ base::Bind(&CookiesDisabled::OnResult, base::Unretained(this)));
+ }
+
+ void OnResult(std::unique_ptr<runtime::EvaluateResult> result) {
+ std::string value;
+ EXPECT_TRUE(result->GetResult()->HasValue());
+ EXPECT_TRUE(result->GetResult()->GetValue()->GetAsString(&value));
+ EXPECT_EQ("-1", value);
+ FinishAsynchronousTest();
+ }
+
+ void CustomizeHeadlessBrowserContext(
+ HeadlessBrowserContext::Builder& builder) override {
+ builder.SetAllowCookies(false);
+ }
+};
+
+HEADLESS_ASYNC_DEVTOOLED_TEST_F(CookiesDisabled);
+
} // namespace headless
diff --git a/chromium/headless/lib/renderer/headless_content_renderer_client.cc b/chromium/headless/lib/renderer/headless_content_renderer_client.cc
index ce73e4d1ac9..edde26667f8 100644
--- a/chromium/headless/lib/renderer/headless_content_renderer_client.cc
+++ b/chromium/headless/lib/renderer/headless_content_renderer_client.cc
@@ -15,9 +15,9 @@
namespace headless {
-HeadlessContentRendererClient::HeadlessContentRendererClient() {}
+HeadlessContentRendererClient::HeadlessContentRendererClient() = default;
-HeadlessContentRendererClient::~HeadlessContentRendererClient() {}
+HeadlessContentRendererClient::~HeadlessContentRendererClient() = default;
void HeadlessContentRendererClient::RenderFrameCreated(
content::RenderFrame* render_frame) {
diff --git a/chromium/headless/lib/renderer/headless_print_render_frame_helper_delegate.cc b/chromium/headless/lib/renderer/headless_print_render_frame_helper_delegate.cc
index b3dc8acf2dc..1b0995d8699 100644
--- a/chromium/headless/lib/renderer/headless_print_render_frame_helper_delegate.cc
+++ b/chromium/headless/lib/renderer/headless_print_render_frame_helper_delegate.cc
@@ -9,10 +9,10 @@
namespace headless {
HeadlessPrintRenderFrameHelperDelegate::
- HeadlessPrintRenderFrameHelperDelegate() {}
+ HeadlessPrintRenderFrameHelperDelegate() = default;
HeadlessPrintRenderFrameHelperDelegate::
- ~HeadlessPrintRenderFrameHelperDelegate() {}
+ ~HeadlessPrintRenderFrameHelperDelegate() = default;
bool HeadlessPrintRenderFrameHelperDelegate::CancelPrerender(
content::RenderFrame* render_frame) {
@@ -37,10 +37,4 @@ bool HeadlessPrintRenderFrameHelperDelegate::IsAskPrintSettingsEnabled() {
return true;
}
-#if defined(OS_MACOSX)
-bool HeadlessPrintRenderFrameHelperDelegate::UseSingleMetafile() {
- return true;
-}
-#endif
-
} // namespace headless
diff --git a/chromium/headless/lib/renderer/headless_print_render_frame_helper_delegate.h b/chromium/headless/lib/renderer/headless_print_render_frame_helper_delegate.h
index 7e7b3136325..c23a7e98dfb 100644
--- a/chromium/headless/lib/renderer/headless_print_render_frame_helper_delegate.h
+++ b/chromium/headless/lib/renderer/headless_print_render_frame_helper_delegate.h
@@ -5,7 +5,7 @@
#ifndef HEADLESS_LIB_RENDERER_HEADLESS_PRINT_RENDER_FRAME_HELPER_DELEGATE_H_
#define HEADLESS_LIB_RENDERER_HEADLESS_PRINT_RENDER_FRAME_HELPER_DELEGATE_H_
-#include "build/build_config.h"
+#include "base/macros.h"
#include "components/printing/renderer/print_render_frame_helper.h"
namespace headless {
@@ -23,9 +23,7 @@ class HeadlessPrintRenderFrameHelperDelegate
bool IsAskPrintSettingsEnabled() override;
blink::WebElement GetPdfElement(blink::WebLocalFrame* frame) override;
-#if defined(OS_MACOSX)
- bool UseSingleMetafile() override;
-#endif
+ DISALLOW_COPY_AND_ASSIGN(HeadlessPrintRenderFrameHelperDelegate);
};
} // namespace headless
diff --git a/chromium/headless/lib/renderer/headless_render_frame_controller_impl.cc b/chromium/headless/lib/renderer/headless_render_frame_controller_impl.cc
index b6761a6d361..9f695f69402 100644
--- a/chromium/headless/lib/renderer/headless_render_frame_controller_impl.cc
+++ b/chromium/headless/lib/renderer/headless_render_frame_controller_impl.cc
@@ -21,7 +21,8 @@ HeadlessRenderFrameControllerImpl::HeadlessRenderFrameControllerImpl(
base::Unretained(this)));
}
-HeadlessRenderFrameControllerImpl::~HeadlessRenderFrameControllerImpl() {}
+HeadlessRenderFrameControllerImpl::~HeadlessRenderFrameControllerImpl() =
+ default;
void HeadlessRenderFrameControllerImpl::OnRenderFrameControllerRequest(
HeadlessRenderFrameControllerRequest request) {
diff --git a/chromium/headless/lib/renderer/headless_tab_socket_bindings.cc b/chromium/headless/lib/renderer/headless_tab_socket_bindings.cc
index 94198ab19ab..247ed07012e 100644
--- a/chromium/headless/lib/renderer/headless_tab_socket_bindings.cc
+++ b/chromium/headless/lib/renderer/headless_tab_socket_bindings.cc
@@ -22,7 +22,7 @@ HeadlessTabSocketBindings::HeadlessTabSocketBindings(
world_id_(world_id),
installed_(false) {}
-HeadlessTabSocketBindings::~HeadlessTabSocketBindings() {}
+HeadlessTabSocketBindings::~HeadlessTabSocketBindings() = default;
bool HeadlessTabSocketBindings::InitializeTabSocketBindings() {
if (installed_)
diff --git a/chromium/headless/lib/utility/headless_content_utility_client.cc b/chromium/headless/lib/utility/headless_content_utility_client.cc
index a2ddaee3d87..d0e5f19d889 100644
--- a/chromium/headless/lib/utility/headless_content_utility_client.cc
+++ b/chromium/headless/lib/utility/headless_content_utility_client.cc
@@ -17,7 +17,7 @@ HeadlessContentUtilityClient::HeadlessContentUtilityClient(
const std::string& user_agent)
: user_agent_(user_agent) {}
-HeadlessContentUtilityClient::~HeadlessContentUtilityClient() {}
+HeadlessContentUtilityClient::~HeadlessContentUtilityClient() = default;
void HeadlessContentUtilityClient::RegisterServices(
HeadlessContentUtilityClient::StaticServiceMap* services) {
diff --git a/chromium/headless/lib/virtual_time_browsertest.cc b/chromium/headless/lib/virtual_time_browsertest.cc
index c4a5af7a012..7a0a6a6059f 100644
--- a/chromium/headless/lib/virtual_time_browsertest.cc
+++ b/chromium/headless/lib/virtual_time_browsertest.cc
@@ -3,17 +3,20 @@
// found in the LICENSE file.
#include <memory>
+#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "content/public/test/browser_test.h"
#include "headless/public/devtools/domains/emulation.h"
#include "headless/public/devtools/domains/page.h"
#include "headless/public/devtools/domains/runtime.h"
#include "headless/public/headless_devtools_client.h"
+#include "headless/public/util/testing/test_in_memory_protocol_handler.h"
#include "headless/test/headless_browser_test.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
+using testing::ElementsAre;
using testing::Contains;
namespace headless {
@@ -23,8 +26,11 @@ class VirtualTimeBrowserTest : public HeadlessAsyncDevTooledBrowserTest,
public page::ExperimentalObserver,
public runtime::Observer {
public:
+ void SetInitialURL(const std::string& initial_url) {
+ initial_url_ = initial_url;
+ }
+
void RunDevTooledTest() override {
- EXPECT_TRUE(embedded_test_server()->Start());
devtools_client_->GetEmulation()->GetExperimental()->AddObserver(this);
devtools_client_->GetPage()->GetExperimental()->AddObserver(this);
devtools_client_->GetRuntime()->AddObserver(this);
@@ -45,7 +51,7 @@ class VirtualTimeBrowserTest : public HeadlessAsyncDevTooledBrowserTest,
MaybeSetVirtualTimePolicy();
}
- void MaybeSetVirtualTimePolicy() {
+ virtual void MaybeSetVirtualTimePolicy() {
if (!page_enabled || !runtime_enabled)
return;
@@ -59,17 +65,17 @@ class VirtualTimeBrowserTest : public HeadlessAsyncDevTooledBrowserTest,
}
void SetVirtualTimePolicyDone(
- std::unique_ptr<emulation::SetVirtualTimePolicyResult>) {
+ std::unique_ptr<emulation::SetVirtualTimePolicyResult> result) {
+ EXPECT_LT(0, result->GetVirtualTimeBase());
// Virtual time is paused, so start navigating.
- devtools_client_->GetPage()->Navigate(
- embedded_test_server()->GetURL("/virtual_time_test.html").spec());
+ devtools_client_->GetPage()->Navigate(initial_url_);
}
void OnFrameStartedLoading(
const page::FrameStartedLoadingParams& params) override {
- if (intial_load_seen_)
+ if (initial_load_seen_)
return;
- intial_load_seen_ = true;
+ initial_load_seen_ = true;
// The navigation is underway, so allow virtual time to advance while
// network fetches are not pending.
devtools_client_->GetEmulation()->GetExperimental()->SetVirtualTimePolicy(
@@ -86,11 +92,23 @@ class VirtualTimeBrowserTest : public HeadlessAsyncDevTooledBrowserTest,
// We expect the arguments always to be a single string.
const std::vector<std::unique_ptr<runtime::RemoteObject>>& args =
*params.GetArgs();
- std::string message;
- if (args.size() == 1u && args[0]->HasValue() &&
- args[0]->GetValue()->GetAsString(&message)) {
- log_.push_back(message);
- }
+ if (args.size() == 1u && args[0]->HasValue())
+ log_.push_back(args[0]->GetValue()->GetString());
+ }
+
+ std::string initial_url_;
+ std::vector<std::string> log_;
+ bool initial_load_seen_ = false;
+ bool page_enabled = false;
+ bool runtime_enabled = false;
+};
+
+class VirtualTimeObserverTest : public VirtualTimeBrowserTest {
+ public:
+ VirtualTimeObserverTest() {
+ EXPECT_TRUE(embedded_test_server()->Start());
+ SetInitialURL(
+ embedded_test_server()->GetURL("/virtual_time_test.html").spec());
}
// emulation::Observer implementation:
@@ -120,22 +138,465 @@ class VirtualTimeBrowserTest : public HeadlessAsyncDevTooledBrowserTest,
void OnVirtualTimeAdvanced(
const emulation::VirtualTimeAdvancedParams& params) override {
- log_.push_back(
- base::StringPrintf("Advanced to %dms", params.GetVirtualTimeElapsed()));
+ log_.push_back(base::StringPrintf("Advanced to %.fms",
+ params.GetVirtualTimeElapsed()));
}
void OnVirtualTimePaused(
const emulation::VirtualTimePausedParams& params) override {
log_.push_back(
- base::StringPrintf("Paused @ %dms", params.GetVirtualTimeElapsed()));
+ base::StringPrintf("Paused @ %.fms", params.GetVirtualTimeElapsed()));
}
+};
- std::vector<std::string> log_;
- bool intial_load_seen_ = false;
- bool page_enabled = false;
- bool runtime_enabled = false;
+HEADLESS_ASYNC_DEVTOOLED_TEST_F(VirtualTimeObserverTest);
+
+class MaxVirtualTimeTaskStarvationCountTest : public VirtualTimeBrowserTest {
+ public:
+ MaxVirtualTimeTaskStarvationCountTest() {
+ EXPECT_TRUE(embedded_test_server()->Start());
+ SetInitialURL(embedded_test_server()
+ ->GetURL("/virtual_time_starvation_test.html")
+ .spec());
+ }
+
+ void OnFrameStartedLoading(
+ const page::FrameStartedLoadingParams& params) override {
+ if (initial_load_seen_)
+ return;
+ initial_load_seen_ = true;
+ // The navigation is underway, so allow virtual time to advance while
+ // network fetches are not pending.
+ devtools_client_->GetEmulation()->GetExperimental()->SetVirtualTimePolicy(
+ emulation::SetVirtualTimePolicyParams::Builder()
+ .SetPolicy(
+ emulation::VirtualTimePolicy::PAUSE_IF_NETWORK_FETCHES_PENDING)
+ .SetBudget(4000)
+ .SetMaxVirtualTimeTaskStarvationCount(100)
+ .Build());
+ }
+
+ // emulation::Observer implementation:
+ void OnVirtualTimeBudgetExpired(
+ const emulation::VirtualTimeBudgetExpiredParams& params) override {
+ // If SetMaxVirtualTimeTaskStarvationCount was not set, this callback would
+ // never fire.
+ FinishAsynchronousTest();
+ }
+};
+
+HEADLESS_ASYNC_DEVTOOLED_TEST_F(MaxVirtualTimeTaskStarvationCountTest);
+
+namespace {
+static constexpr char kIndexHtml[] = R"(
+<html>
+<body>
+<iframe src="/iframe.html" width="400" height="200" id="iframe1">
+</iframe>
+</body>
+</html>
+)";
+
+static constexpr char kIFrame[] = R"(
+<html>
+<head>
+<link rel="stylesheet" type="text/css" href="style.css">
+</head>
+<body>
+<h1>Hello from the iframe!</h1>
+</body>
+</html>
+)";
+
+static constexpr char kCss[] = R"(
+.test {
+ color: blue;
+}
+)";
+
+} // namespace
+
+class FrameDetatchWithPendingResourceLoadVirtualTimeTest
+ : public VirtualTimeBrowserTest,
+ public TestInMemoryProtocolHandler::RequestDeferrer {
+ public:
+ FrameDetatchWithPendingResourceLoadVirtualTimeTest() {
+ SetInitialURL("http://test.com/index.html");
+ }
+
+ ProtocolHandlerMap GetProtocolHandlers() override {
+ ProtocolHandlerMap protocol_handlers;
+ std::unique_ptr<TestInMemoryProtocolHandler> http_handler(
+ new TestInMemoryProtocolHandler(browser()->BrowserIOThread(), this));
+ http_handler_ = http_handler.get();
+ http_handler->InsertResponse("http://test.com/index.html",
+ {kIndexHtml, "text/html"});
+ http_handler->InsertResponse("http://test.com/iframe.html",
+ {kIFrame, "text/html"});
+ http_handler->InsertResponse("http://test.com/style.css",
+ {kCss, "text/css"});
+ protocol_handlers[url::kHttpScheme] = std::move(http_handler);
+ return protocol_handlers;
+ }
+
+ void RunDevTooledTest() override {
+ http_handler_->SetHeadlessBrowserContext(browser_context_);
+ VirtualTimeBrowserTest::RunDevTooledTest();
+ }
+
+ void OnRequest(const GURL& url, base::Closure complete_request) override {
+ // Note this is called on the IO thread.
+ if (url.spec() == "http://test.com/style.css") {
+ // Detach the iframe but leave the css resource fetch hanging.
+ browser()->BrowserMainThread()->PostTask(
+ FROM_HERE,
+ base::Bind(&FrameDetatchWithPendingResourceLoadVirtualTimeTest::
+ DetatchIFrame,
+ base::Unretained(this)));
+ } else {
+ complete_request.Run();
+ }
+ }
+
+ void DetatchIFrame() {
+ devtools_client_->GetRuntime()->Evaluate(
+ "let elem = document.getElementById('iframe1');\n"
+ "elem.parentNode.removeChild(elem);");
+ }
+
+ // emulation::Observer implementation:
+ void OnVirtualTimeBudgetExpired(
+ const emulation::VirtualTimeBudgetExpiredParams& params) override {
+ EXPECT_THAT(
+ http_handler_->urls_requested(),
+ ElementsAre("http://test.com/index.html", "http://test.com/iframe.html",
+ "http://test.com/style.css"));
+
+ // Virtual time should still expire, despite the CSS resource load not
+ // finishing.
+ FinishAsynchronousTest();
+ }
+
+ TestInMemoryProtocolHandler* http_handler_; // NOT OWNED
+};
+
+HEADLESS_ASYNC_DEVTOOLED_TEST_F(
+ FrameDetatchWithPendingResourceLoadVirtualTimeTest);
+
+class VirtualTimeLocalStorageTest : public VirtualTimeBrowserTest {
+ public:
+ VirtualTimeLocalStorageTest() {
+ EXPECT_TRUE(embedded_test_server()->Start());
+ SetInitialURL(embedded_test_server()
+ ->GetURL("/virtual_time_local_storage.html")
+ .spec());
+ }
+
+ void OnFrameStartedLoading(
+ const page::FrameStartedLoadingParams& params) override {
+ if (initial_load_seen_)
+ return;
+ initial_load_seen_ = true;
+ // The navigation is underway, so allow virtual time to advance while
+ // network fetches are not pending.
+ devtools_client_->GetEmulation()->GetExperimental()->SetVirtualTimePolicy(
+ emulation::SetVirtualTimePolicyParams::Builder()
+ .SetPolicy(
+ emulation::VirtualTimePolicy::PAUSE_IF_NETWORK_FETCHES_PENDING)
+ .SetBudget(4001)
+ .SetMaxVirtualTimeTaskStarvationCount(100)
+ .Build());
+ }
+
+ void OnConsoleAPICalled(
+ const runtime::ConsoleAPICalledParams& params) override {
+ EXPECT_EQ(runtime::ConsoleAPICalledType::LOG, params.GetType());
+ ASSERT_EQ(1u, params.GetArgs()->size());
+ ASSERT_EQ(runtime::RemoteObjectType::STRING,
+ (*params.GetArgs())[0]->GetType());
+ std::string count_string = (*params.GetArgs())[0]->GetValue()->GetString();
+ int count;
+ ASSERT_TRUE(base::StringToInt(count_string, &count)) << count_string;
+ // We don't care what exact number |count| has as long as it's not too small
+ // or too large.
+ EXPECT_GT(count, 100);
+ EXPECT_LT(count, 1000);
+ console_log_seen_ = true;
+ }
+
+ // emulation::Observer implementation:
+ void OnVirtualTimeBudgetExpired(
+ const emulation::VirtualTimeBudgetExpiredParams& params) override {
+ // If SetMaxVirtualTimeTaskStarvationCount was not set, this callback would
+ // never fire.
+ EXPECT_TRUE(console_log_seen_);
+ FinishAsynchronousTest();
+ }
+
+ bool console_log_seen_ = false;
+};
+
+HEADLESS_ASYNC_DEVTOOLED_TEST_F(VirtualTimeLocalStorageTest);
+
+class VirtualTimeSessionStorageTest : public VirtualTimeBrowserTest {
+ public:
+ VirtualTimeSessionStorageTest() {
+ EXPECT_TRUE(embedded_test_server()->Start());
+ SetInitialURL(embedded_test_server()
+ ->GetURL("/virtual_time_session_storage.html")
+ .spec());
+ }
+
+ void OnFrameStartedLoading(
+ const page::FrameStartedLoadingParams& params) override {
+ if (initial_load_seen_)
+ return;
+ initial_load_seen_ = true;
+ // The navigation is underway, so allow virtual time to advance while
+ // network fetches are not pending.
+ devtools_client_->GetEmulation()->GetExperimental()->SetVirtualTimePolicy(
+ emulation::SetVirtualTimePolicyParams::Builder()
+ .SetPolicy(
+ emulation::VirtualTimePolicy::PAUSE_IF_NETWORK_FETCHES_PENDING)
+ .SetBudget(4001)
+ .SetMaxVirtualTimeTaskStarvationCount(100)
+ .Build());
+ }
+
+ void OnConsoleAPICalled(
+ const runtime::ConsoleAPICalledParams& params) override {
+ EXPECT_EQ(runtime::ConsoleAPICalledType::LOG, params.GetType());
+ ASSERT_EQ(1u, params.GetArgs()->size());
+ ASSERT_EQ(runtime::RemoteObjectType::STRING,
+ (*params.GetArgs())[0]->GetType());
+ std::string count_string = (*params.GetArgs())[0]->GetValue()->GetString();
+ int count;
+ ASSERT_TRUE(base::StringToInt(count_string, &count)) << count_string;
+ // We don't care what exact number |count| has as long as it's not too small
+ // or too large.
+ EXPECT_GT(count, 100);
+ EXPECT_LT(count, 1000);
+ console_log_seen_ = true;
+ }
+
+ // emulation::Observer implementation:
+ void OnVirtualTimeBudgetExpired(
+ const emulation::VirtualTimeBudgetExpiredParams& params) override {
+ // If SetMaxVirtualTimeTaskStarvationCount was not set, this callback would
+ // never fire.
+ EXPECT_TRUE(console_log_seen_);
+ FinishAsynchronousTest();
+ }
+
+ bool console_log_seen_ = false;
+};
+
+HEADLESS_ASYNC_DEVTOOLED_TEST_F(VirtualTimeSessionStorageTest);
+
+class DeferredLoadDoesntBlockVirtualTimeTest : public VirtualTimeBrowserTest {
+ public:
+ DeferredLoadDoesntBlockVirtualTimeTest() {
+ EXPECT_TRUE(embedded_test_server()->Start());
+ SetInitialURL(embedded_test_server()->GetURL("/video.html").spec());
+ }
+
+ // emulation::Observer implementation:
+ void OnVirtualTimeBudgetExpired(
+ const emulation::VirtualTimeBudgetExpiredParams& params) override {
+ // The video should not block virtual time.
+ FinishAsynchronousTest();
+ }
+};
+
+HEADLESS_ASYNC_DEVTOOLED_TEST_F(DeferredLoadDoesntBlockVirtualTimeTest);
+
+class RedirectVirtualTimeTest : public VirtualTimeBrowserTest {
+ public:
+ RedirectVirtualTimeTest() { SetInitialURL("http://test.com/index.html"); }
+
+ ProtocolHandlerMap GetProtocolHandlers() override {
+ ProtocolHandlerMap protocol_handlers;
+ std::unique_ptr<TestInMemoryProtocolHandler> http_handler(
+ new TestInMemoryProtocolHandler(browser()->BrowserIOThread(), nullptr));
+ http_handler_ = http_handler.get();
+ http_handler->InsertResponse("http://test.com/index.html",
+ {kIndexHtml, "text/html"});
+ http_handler->InsertResponse(
+ "http://test.com/iframe.html",
+ {"HTTP/1.1 302 Found\r\nLocation: iframe2.html\r\n\r\n"});
+ http_handler->InsertResponse("http://test.com/iframe2.html",
+ {kIFrame, "text/html"});
+ http_handler->InsertResponse(
+ "http://test.com/style.css",
+ {"HTTP/1.1 302 Found\r\nLocation: style2.css\r\n\r\n"});
+ http_handler->InsertResponse("http://test.com/style2.css",
+ {kCss, "text/css"});
+ protocol_handlers[url::kHttpScheme] = std::move(http_handler);
+ return protocol_handlers;
+ }
+
+ void RunDevTooledTest() override {
+ http_handler_->SetHeadlessBrowserContext(browser_context_);
+ VirtualTimeBrowserTest::RunDevTooledTest();
+ }
+
+ // emulation::Observer implementation:
+ void OnVirtualTimeBudgetExpired(
+ const emulation::VirtualTimeBudgetExpiredParams& params) override {
+ EXPECT_THAT(
+ http_handler_->urls_requested(),
+ ElementsAre("http://test.com/index.html", "http://test.com/iframe.html",
+ "http://test.com/iframe2.html", "http://test.com/style.css",
+ "http://test.com/style2.css"));
+
+ // Virtual time should still expire, despite the CSS resource load not
+ // finishing.
+ FinishAsynchronousTest();
+ }
+
+ TestInMemoryProtocolHandler* http_handler_; // NOT OWNED
+};
+
+HEADLESS_ASYNC_DEVTOOLED_TEST_F(RedirectVirtualTimeTest);
+
+namespace {
+static constexpr char kFooDotCom[] = R"(
+<html>
+<body>
+<script> console.log(document.location.href); </script>
+<iframe src='/a/'></iframe>\n"
+</body>
+</html>
+)";
+
+static constexpr char kFooDotComSlashA[] = R"(
+<html>
+<body>
+<script> console.log(document.location.href); </script>
+</body>
+</html>
+)";
+
+static constexpr char kBarDotCom[] = R"(
+<html>
+<body>
+<script> console.log(document.location.href); </script>
+<iframe src='/b/' id='frame_b'></iframe>
+<iframe src='/c/'></iframe>
+</body>
+</html>
+)";
+
+static constexpr char kBarDotComSlashB[] = R"(
+<html>
+<body>
+<script> console.log(document.location.href); </script>
+<iframe src='/d/'></iframe>
+</body>
+</html>
+)";
+
+static constexpr char kBarDotComSlashC[] = R"(
+<html>
+<body>
+<script> console.log(document.location.href); </script>
+</body>
+</html>
+)";
+
+static constexpr char kBarDotComSlashD[] = R"(
+<html>
+<body>
+<script> console.log(document.location.href); </script>
+</body>
+</html>
+)";
+
+static constexpr char kBarDotComSlashE[] = R"(
+<html>
+<body>
+<script> console.log(document.location.href); </script>
+<iframe src='/f/'></iframe>
+</body>
+</html>
+)";
+
+static constexpr char kBarDotComSlashF[] = R"(
+<html>
+<body>
+<script> console.log(document.location.href); </script>
+</body>
+</html>
+)";
+
+} // namespace
+
+class VirtualTimeAndHistoryNavigationTest : public VirtualTimeBrowserTest {
+ public:
+ VirtualTimeAndHistoryNavigationTest() { SetInitialURL("http://foo.com/"); }
+
+ ProtocolHandlerMap GetProtocolHandlers() override {
+ ProtocolHandlerMap protocol_handlers;
+ std::unique_ptr<TestInMemoryProtocolHandler> http_handler(
+ new TestInMemoryProtocolHandler(browser()->BrowserIOThread(), nullptr));
+ http_handler_ = http_handler.get();
+ http_handler->InsertResponse("http://foo.com/", {kFooDotCom, "text/html"});
+ http_handler->InsertResponse("http://foo.com/a/",
+ {kFooDotComSlashA, "text/html"});
+ http_handler->InsertResponse("http://bar.com/", {kBarDotCom, "text/html"});
+ http_handler->InsertResponse("http://bar.com/b/",
+ {kBarDotComSlashB, "text/html"});
+ http_handler->InsertResponse("http://bar.com/c/",
+ {kBarDotComSlashC, "text/html"});
+ http_handler->InsertResponse("http://bar.com/d/",
+ {kBarDotComSlashD, "text/html"});
+ http_handler->InsertResponse("http://bar.com/e/",
+ {kBarDotComSlashE, "text/html"});
+ http_handler->InsertResponse("http://bar.com/f/",
+ {kBarDotComSlashF, "text/html"});
+ protocol_handlers[url::kHttpScheme] = std::move(http_handler);
+ return protocol_handlers;
+ }
+
+ void RunDevTooledTest() override {
+ http_handler_->SetHeadlessBrowserContext(browser_context_);
+ VirtualTimeBrowserTest::RunDevTooledTest();
+ }
+
+ // emulation::Observer implementation:
+ void OnVirtualTimeBudgetExpired(
+ const emulation::VirtualTimeBudgetExpiredParams& params) override {
+ if (step_ < test_commands_.size()) {
+ devtools_client_->GetRuntime()->Evaluate(
+ test_commands_[step_++],
+ base::Bind(&VirtualTimeAndHistoryNavigationTest::OnEvaluateResult,
+ base::Unretained(this)));
+ } else {
+ FinishAsynchronousTest();
+ }
+ }
+
+ void OnEvaluateResult(std::unique_ptr<runtime::EvaluateResult>) {
+ devtools_client_->GetEmulation()->GetExperimental()->SetVirtualTimePolicy(
+ emulation::SetVirtualTimePolicyParams::Builder()
+ .SetPolicy(
+ emulation::VirtualTimePolicy::PAUSE_IF_NETWORK_FETCHES_PENDING)
+ .SetBudget(5000)
+ .Build());
+ }
+
+ const std::vector<std::string> test_commands_ = {
+ "document.location.href = 'http://bar.com/'",
+ "document.getElementById('frame_b').src = '/e/'",
+ "history.back()",
+ "history.forward()",
+ "history.go(-1)",
+ };
+
+ size_t step_ = 0;
+ TestInMemoryProtocolHandler* http_handler_; // NOT OWNED
};
-HEADLESS_ASYNC_DEVTOOLED_TEST_F(VirtualTimeBrowserTest);
+HEADLESS_ASYNC_DEVTOOLED_TEST_F(VirtualTimeAndHistoryNavigationTest);
} // namespace headless
diff --git a/chromium/headless/public/headless_browser.cc b/chromium/headless/public/headless_browser.cc
index 1f01f5a3f87..7e124fc32b4 100644
--- a/chromium/headless/public/headless_browser.cc
+++ b/chromium/headless/public/headless_browser.cc
@@ -31,16 +31,6 @@ std::string GetProductNameAndVersion() {
Options::Options(int argc, const char** argv)
: argc(argc),
argv(argv),
-#if defined(OS_WIN)
- instance(0),
- sandbox_info(nullptr),
-#endif
- devtools_endpoint(),
- devtools_socket_fd(0),
- message_pump(nullptr),
- single_process_mode(false),
- disable_sandbox(false),
- enable_resource_scheduler(true),
#if defined(USE_OZONE)
// TODO(skyostil): Implement SwiftShader backend for headless ozone.
gl_implementation("osmesa"),
@@ -54,14 +44,12 @@ Options::Options(int argc, const char** argv)
#endif
product_name_and_version(GetProductNameAndVersion()),
user_agent(content::BuildUserAgentFromProduct(product_name_and_version)),
- window_size(kDefaultWindowSize),
- incognito_mode(true),
- enable_crash_reporter(false) {
+ window_size(kDefaultWindowSize) {
}
Options::Options(Options&& options) = default;
-Options::~Options() {}
+Options::~Options() = default;
Options& Options::operator=(Options&& options) = default;
@@ -73,7 +61,7 @@ Builder::Builder(int argc, const char** argv) : options_(argc, argv) {}
Builder::Builder() : options_(0, nullptr) {}
-Builder::~Builder() {}
+Builder::~Builder() = default;
Builder& Builder::SetProductNameAndVersion(
const std::string& product_name_and_version) {
@@ -142,6 +130,12 @@ Builder& Builder::AddMojoServiceName(const std::string& mojo_service_name) {
return *this;
}
+Builder& Builder::SetAppendCommandLineFlagsCallback(
+ const Options::AppendCommandLineFlagsCallback& callback) {
+ options_.append_command_line_flags_callback = callback;
+ return *this;
+}
+
#if defined(OS_WIN)
Builder& Builder::SetInstance(HINSTANCE instance) {
options_.instance = instance;
@@ -170,7 +164,7 @@ Builder& Builder::SetIncognitoMode(bool incognito_mode) {
}
Builder& Builder::SetOverrideWebPreferencesCallback(
- base::Callback<void(WebPreferences*)> callback) {
+ const base::Callback<void(WebPreferences*)>& callback) {
options_.override_web_preferences_callback = callback;
return *this;
}
diff --git a/chromium/headless/public/headless_browser.h b/chromium/headless/public/headless_browser.h
index d3ef26dcd76..3ce4f82a72c 100644
--- a/chromium/headless/public/headless_browser.h
+++ b/chromium/headless/public/headless_browser.h
@@ -12,6 +12,7 @@
#include <vector>
#include "base/callback.h"
+#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
@@ -102,16 +103,16 @@ struct HEADLESS_EXPORT HeadlessBrowser::Options {
Options& operator=(Options&& options);
- // Command line options to be passed to browser.
+ // Command line options to be passed to browser. Initialized in constructor.
int argc;
const char** argv;
#if defined(OS_WIN)
// Set hardware instance if available, otherwise it defaults to 0.
- HINSTANCE instance;
+ HINSTANCE instance = 0;
// Set with sandbox information. This has to be already initialized.
- sandbox::SandboxInterfaceInfo* sandbox_info;
+ sandbox::SandboxInterfaceInfo* sandbox_info = nullptr;
#endif
// Address at which DevTools should listen for connections. Disabled by
@@ -120,25 +121,25 @@ struct HEADLESS_EXPORT HeadlessBrowser::Options {
// The fd of an already-open socket inherited from a parent process. Disabled
// by default. Mutually exclusive with devtools_endpoint.
- size_t devtools_socket_fd;
+ size_t devtools_socket_fd = 0;
// A single way to test whether the devtools server has been requested.
bool DevtoolsServerEnabled();
// Optional message pump that overrides the default. Must outlive the browser.
- base::MessagePump* message_pump;
+ base::MessagePump* message_pump = nullptr;
// Run the browser in single process mode instead of using separate renderer
// processes as per default. Note that this also disables any sandboxing of
// web content, which can be a security risk.
- bool single_process_mode;
+ bool single_process_mode = false;
// Run the browser without renderer sandbox. This option can be
// a security risk and should be used with caution.
- bool disable_sandbox;
+ bool disable_sandbox = false;
// Whether or not to enable content::ResourceScheduler. Enabled by default.
- bool enable_resource_scheduler;
+ bool enable_resource_scheduler = true;
// Choose the GL implementation to use for rendering. A suitable
// implementantion is selected by default. Setting this to an empty
@@ -156,7 +157,7 @@ struct HEADLESS_EXPORT HeadlessBrowser::Options {
std::string user_agent;
// The ProxyConfig to use. The system proxy settings are used by default.
- std::unique_ptr<net::ProxyConfig> proxy_config;
+ std::unique_ptr<net::ProxyConfig> proxy_config = nullptr;
// Comma-separated list of rules that control how hostnames are mapped. See
// chrome::switches::kHostRules for a description for the format.
@@ -171,7 +172,10 @@ struct HEADLESS_EXPORT HeadlessBrowser::Options {
base::FilePath user_data_dir;
// Run a browser context in an incognito mode. Enabled by default.
- bool incognito_mode;
+ bool incognito_mode = true;
+
+ // Whether cookies are allowed. Enabled by default.
+ bool allow_cookies = true;
// Set a callback that is invoked to override WebPreferences for RenderViews
// created within the HeadlessBrowser. Called whenever the WebPreferences of a
@@ -181,10 +185,25 @@ struct HEADLESS_EXPORT HeadlessBrowser::Options {
// exposed WebPreferences API, so use with care.
base::Callback<void(WebPreferences*)> override_web_preferences_callback;
+ // Set a callback that is invoked when a new child process is spawned or
+ // forked and allows adding additional command line flags to the child
+ // process's command line. Executed on the browser main thread.
+ // |child_browser_context| points to the BrowserContext of the child
+ // process, but will only be set if the child process is a renderer process.
+ //
+ // NOTE: This callback may be called on the UI or IO thread even after the
+ // HeadlessBrowser has been destroyed.
+ using AppendCommandLineFlagsCallback =
+ base::Callback<void(base::CommandLine* command_line,
+ HeadlessBrowserContext* child_browser_context,
+ const std::string& child_process_type,
+ int child_process_id)>;
+ AppendCommandLineFlagsCallback append_command_line_flags_callback;
+
// Minidump crash reporter settings. Crash reporting is disabled by default.
// By default crash dumps are written to the directory containing the
// executable.
- bool enable_crash_reporter;
+ bool enable_crash_reporter = false;
base::FilePath crash_dumps_dir;
// Reminder: when adding a new field here, do not forget to add it to
@@ -211,6 +230,8 @@ class HEADLESS_EXPORT HeadlessBrowser::Options::Builder {
Builder& SetEnableResourceScheduler(bool enable_resource_scheduler);
Builder& SetGLImplementation(const std::string& gl_implementation);
Builder& AddMojoServiceName(const std::string& mojo_service_name);
+ Builder& SetAppendCommandLineFlagsCallback(
+ const Options::AppendCommandLineFlagsCallback& callback);
#if defined(OS_WIN)
Builder& SetInstance(HINSTANCE instance);
Builder& SetSandboxInfo(sandbox::SandboxInterfaceInfo* sandbox_info);
@@ -227,8 +248,9 @@ class HEADLESS_EXPORT HeadlessBrowser::Options::Builder {
Builder& SetWindowSize(const gfx::Size& window_size);
Builder& SetUserDataDir(const base::FilePath& user_data_dir);
Builder& SetIncognitoMode(bool incognito_mode);
+ Builder& SetAllowCookies(bool allow_cookies);
Builder& SetOverrideWebPreferencesCallback(
- base::Callback<void(WebPreferences*)> callback);
+ const base::Callback<void(WebPreferences*)>& callback);
Builder& SetCrashReporterEnabled(bool enabled);
Builder& SetCrashDumpsDir(const base::FilePath& dir);
diff --git a/chromium/headless/public/headless_browser_context.h b/chromium/headless/public/headless_browser_context.h
index 83b1207ea52..1d117decfa6 100644
--- a/chromium/headless/public/headless_browser_context.h
+++ b/chromium/headless/public/headless_browser_context.h
@@ -14,6 +14,7 @@
#include "base/callback.h"
#include "base/optional.h"
#include "content/public/common/web_preferences.h"
+#include "headless/lib/browser/headless_network_conditions.h"
#include "headless/public/headless_export.h"
#include "headless/public/headless_web_contents.h"
#include "net/proxy/proxy_service.h"
@@ -69,6 +70,8 @@ class HEADLESS_EXPORT HeadlessBrowserContext {
virtual void AddObserver(Observer* observer) = 0;
virtual void RemoveObserver(Observer* observer) = 0;
+ virtual HeadlessNetworkConditions GetNetworkConditions() = 0;
+
// TODO(skyostil): Allow saving and restoring contexts (crbug.com/617931).
protected:
@@ -132,6 +135,7 @@ class HEADLESS_EXPORT HeadlessBrowserContext::Builder {
Builder& SetWindowSize(const gfx::Size& window_size);
Builder& SetUserDataDir(const base::FilePath& user_data_dir);
Builder& SetIncognitoMode(bool incognito_mode);
+ Builder& SetAllowCookies(bool incognito_mode);
Builder& SetOverrideWebPreferencesCallback(
base::Callback<void(WebPreferences*)> callback);
diff --git a/chromium/headless/public/headless_devtools_client.h b/chromium/headless/public/headless_devtools_client.h
index 88b11826ee3..ee4a336f25f 100644
--- a/chromium/headless/public/headless_devtools_client.h
+++ b/chromium/headless/public/headless_devtools_client.h
@@ -10,6 +10,10 @@
#include "base/macros.h"
#include "headless/public/headless_export.h"
+namespace base {
+class DictionaryValue;
+} // namespace base
+
namespace headless {
namespace accessibility {
@@ -90,6 +94,9 @@ class Domain;
namespace page {
class Domain;
}
+namespace performance {
+class Domain;
+}
namespace profiler {
class Domain;
}
@@ -147,6 +154,7 @@ class HEADLESS_EXPORT HeadlessDevToolsClient {
virtual memory::Domain* GetMemory() = 0;
virtual network::Domain* GetNetwork() = 0;
virtual page::Domain* GetPage() = 0;
+ virtual performance::Domain* GetPerformance() = 0;
virtual profiler::Domain* GetProfiler() = 0;
virtual runtime::Domain* GetRuntime() = 0;
virtual security::Domain* GetSecurity() = 0;
diff --git a/chromium/headless/public/internal/headless_devtools_client_impl.h b/chromium/headless/public/internal/headless_devtools_client_impl.h
index ed1eb70efcd..d9b89eae511 100644
--- a/chromium/headless/public/internal/headless_devtools_client_impl.h
+++ b/chromium/headless/public/internal/headless_devtools_client_impl.h
@@ -36,6 +36,7 @@
#include "headless/public/devtools/domains/memory.h"
#include "headless/public/devtools/domains/network.h"
#include "headless/public/devtools/domains/page.h"
+#include "headless/public/devtools/domains/performance.h"
#include "headless/public/devtools/domains/profiler.h"
#include "headless/public/devtools/domains/runtime.h"
#include "headless/public/devtools/domains/security.h"
@@ -93,6 +94,7 @@ class HEADLESS_EXPORT HeadlessDevToolsClientImpl
memory::Domain* GetMemory() override;
network::Domain* GetNetwork() override;
page::Domain* GetPage() override;
+ performance::Domain* GetPerformance() override;
profiler::Domain* GetProfiler() override;
runtime::Domain* GetRuntime() override;
security::Domain* GetSecurity() override;
@@ -105,11 +107,10 @@ class HEADLESS_EXPORT HeadlessDevToolsClientImpl
void SendRawDevToolsMessage(const std::string& json_message) override;
void SendRawDevToolsMessage(const base::DictionaryValue& message) override;
- // content::DevToolstAgentHostClient implementation:
+ // content::DevToolsAgentHostClient implementation:
void DispatchProtocolMessage(content::DevToolsAgentHost* agent_host,
const std::string& json_message) override;
- void AgentHostClosed(content::DevToolsAgentHost* agent_host,
- bool replaced_with_another_client) override;
+ void AgentHostClosed(content::DevToolsAgentHost* agent_host) override;
// internal::MessageDispatcher implementation:
void SendMessage(const char* method,
@@ -203,6 +204,7 @@ class HEADLESS_EXPORT HeadlessDevToolsClientImpl
memory::ExperimentalDomain memory_domain_;
network::ExperimentalDomain network_domain_;
page::ExperimentalDomain page_domain_;
+ performance::ExperimentalDomain performance_domain_;
profiler::ExperimentalDomain profiler_domain_;
runtime::ExperimentalDomain runtime_domain_;
security::ExperimentalDomain security_domain_;
diff --git a/chromium/headless/public/internal/value_conversions.h b/chromium/headless/public/internal/value_conversions.h
index f2ded264939..7f218b42da9 100644
--- a/chromium/headless/public/internal/value_conversions.h
+++ b/chromium/headless/public/internal/value_conversions.h
@@ -74,40 +74,44 @@ std::unique_ptr<base::Value> ToValueImpl(const std::unique_ptr<T>& value,
template <>
struct FromValue<bool> {
static bool Parse(const base::Value& value, ErrorReporter* errors) {
- bool result = false;
- if (!value.GetAsBoolean(&result))
+ if (!value.is_bool()) {
errors->AddError("boolean value expected");
- return result;
+ return false;
+ }
+ return value.GetBool();
}
};
template <>
struct FromValue<int> {
static int Parse(const base::Value& value, ErrorReporter* errors) {
- int result = 0;
- if (!value.GetAsInteger(&result))
+ if (!value.is_int()) {
errors->AddError("integer value expected");
- return result;
+ return 0;
+ }
+ return value.GetInt();
}
};
template <>
struct FromValue<double> {
static double Parse(const base::Value& value, ErrorReporter* errors) {
- double result = 0;
- if (!value.GetAsDouble(&result))
+ if (!value.is_double() && !value.is_int()) {
errors->AddError("double value expected");
- return result;
+ return 0;
+ }
+ return value.GetDouble();
}
};
template <>
struct FromValue<std::string> {
static std::string Parse(const base::Value& value, ErrorReporter* errors) {
- std::string result;
- if (!value.GetAsString(&result))
+ if (!value.is_string()) {
errors->AddError("string value expected");
- return result;
+ return "";
+ }
+ return value.GetString();
}
};
@@ -144,13 +148,12 @@ template <typename T>
struct FromValue<std::vector<T>> {
static std::vector<T> Parse(const base::Value& value, ErrorReporter* errors) {
std::vector<T> result;
- const base::ListValue* list;
- if (!value.GetAsList(&list)) {
+ if (!value.is_list()) {
errors->AddError("list value expected");
return result;
}
errors->Push();
- for (const auto& item : *list)
+ for (const auto& item : value.GetList())
result.push_back(FromValue<T>::Parse(item, errors));
errors->Pop();
return result;
diff --git a/chromium/headless/public/util/DEPS b/chromium/headless/public/util/DEPS
new file mode 100644
index 00000000000..5fc6a7b9a6a
--- /dev/null
+++ b/chromium/headless/public/util/DEPS
@@ -0,0 +1,6 @@
+specific_include_rules = {
+ "compositor_controller_browsertest.cc": [
+ "+cc/base/switches.h",
+ ]
+}
+
diff --git a/chromium/headless/public/util/black_hole_protocol_handler.cc b/chromium/headless/public/util/black_hole_protocol_handler.cc
index 3f036c59706..31a269b3328 100644
--- a/chromium/headless/public/util/black_hole_protocol_handler.cc
+++ b/chromium/headless/public/util/black_hole_protocol_handler.cc
@@ -34,7 +34,7 @@ BlackHoleRequestJob::BlackHoleRequestJob(net::URLRequest* request,
net::NetworkDelegate* network_delegate)
: net::URLRequestJob(request, network_delegate), weak_factory_(this) {}
-BlackHoleRequestJob::~BlackHoleRequestJob() {}
+BlackHoleRequestJob::~BlackHoleRequestJob() = default;
void BlackHoleRequestJob::Start() {
base::ThreadTaskRunnerHandle::Get()->PostTask(
@@ -54,8 +54,8 @@ void BlackHoleRequestJob::Kill() {
}
} // namespace
-BlackHoleProtocolHandler::BlackHoleProtocolHandler() {}
-BlackHoleProtocolHandler::~BlackHoleProtocolHandler() {}
+BlackHoleProtocolHandler::BlackHoleProtocolHandler() = default;
+BlackHoleProtocolHandler::~BlackHoleProtocolHandler() = default;
net::URLRequestJob* BlackHoleProtocolHandler::MaybeCreateJob(
net::URLRequest* request,
diff --git a/chromium/headless/public/util/compositor_controller.cc b/chromium/headless/public/util/compositor_controller.cc
new file mode 100644
index 00000000000..2bc803c8532
--- /dev/null
+++ b/chromium/headless/public/util/compositor_controller.cc
@@ -0,0 +1,429 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "headless/public/util/compositor_controller.h"
+
+#include "base/base64.h"
+#include "base/bind.h"
+#include "base/cancelable_callback.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/trace_event/trace_event.h"
+#include "headless/public/util/virtual_time_controller.h"
+
+namespace headless {
+
+// Sends BeginFrames to advance animations while virtual time advances in
+// intervals.
+class CompositorController::AnimationBeginFrameTask
+ : public VirtualTimeController::RepeatingTask {
+ public:
+ explicit AnimationBeginFrameTask(CompositorController* compositor_controller)
+ : compositor_controller_(compositor_controller),
+ weak_ptr_factory_(this) {}
+
+ // VirtualTimeController::RepeatingTask implementation:
+ void IntervalElapsed(base::TimeDelta virtual_time_offset,
+ const base::Closure& continue_callback) override {
+ continue_callback_ = continue_callback;
+
+ // Post a cancellable task that will issue a BeginFrame. This way, we can
+ // cancel sending an animation-only BeginFrame if another virtual time task
+ // sends a screenshotting BeginFrame first, or if the budget was exhausted.
+ // TODO(eseckler): This won't capture screenshot requests sent
+ // asynchronously.
+ begin_frame_task_.Reset(
+ base::Bind(&AnimationBeginFrameTask::IssueAnimationBeginFrame,
+ weak_ptr_factory_.GetWeakPtr()));
+ compositor_controller_->task_runner_->PostTask(
+ FROM_HERE, begin_frame_task_.callback());
+ }
+
+ void BudgetRequested(base::TimeDelta virtual_time_offset,
+ base::TimeDelta requested_budget,
+ const base::Closure& continue_callback) override {
+ // Run a BeginFrame if we cancelled it because the budged expired previously
+ // and no other BeginFrame was sent while virtual time was paused.
+ if (needs_begin_frame_on_virtual_time_resume_) {
+ continue_callback_ = continue_callback;
+ IssueAnimationBeginFrame();
+ return;
+ }
+ continue_callback.Run();
+ }
+
+ void BudgetExpired(base::TimeDelta virtual_time_offset) override {
+ // Wait until a new budget was requested before sending another animation
+ // BeginFrame, as it's likely that we will send a screenshotting BeginFrame.
+ if (!begin_frame_task_.IsCancelled()) {
+ begin_frame_task_.Cancel();
+ needs_begin_frame_on_virtual_time_resume_ = true;
+ BeginFrameComplete(nullptr);
+ }
+ }
+
+ void CompositorControllerIssuingScreenshotBeginFrame() {
+ // The screenshotting BeginFrame will replace our animation-only BeginFrame.
+ // We cancel any pending animation BeginFrame to avoid sending two
+ // BeginFrames within the same virtual time pause.
+ needs_begin_frame_on_virtual_time_resume_ = false;
+ if (!begin_frame_task_.IsCancelled()) {
+ begin_frame_task_.Cancel();
+ BeginFrameComplete(nullptr);
+ }
+ }
+
+ private:
+ void IssueAnimationBeginFrame() {
+ TRACE_EVENT0("headless",
+ "CompositorController::AnimationBeginFrameTask::"
+ "IssueAnimationBeginFrame");
+ begin_frame_task_.Cancel();
+ needs_begin_frame_on_virtual_time_resume_ = false;
+ // No need for PostBeginFrame, since the begin_frame_task_ has already been
+ // posted above.
+ compositor_controller_->BeginFrame(
+ base::Bind(&AnimationBeginFrameTask::BeginFrameComplete,
+ weak_ptr_factory_.GetWeakPtr()));
+ }
+
+ void BeginFrameComplete(std::unique_ptr<BeginFrameResult>) {
+ TRACE_EVENT0(
+ "headless",
+ "CompositorController::AnimationBeginFrameTask::BeginFrameComplete");
+ DCHECK(continue_callback_);
+ auto callback = continue_callback_;
+ continue_callback_.Reset();
+ callback.Run();
+ }
+
+ CompositorController* compositor_controller_; // NOT OWNED
+ bool needs_begin_frame_on_virtual_time_resume_ = false;
+ base::CancelableClosure begin_frame_task_;
+ base::Closure continue_callback_;
+ base::WeakPtrFactory<AnimationBeginFrameTask> weak_ptr_factory_;
+};
+
+CompositorController::CompositorController(
+ scoped_refptr<base::SequencedTaskRunner> task_runner,
+ HeadlessDevToolsClient* devtools_client,
+ VirtualTimeController* virtual_time_controller,
+ base::TimeDelta animation_begin_frame_interval,
+ base::TimeDelta wait_for_compositor_ready_begin_frame_delay)
+ : task_runner_(std::move(task_runner)),
+ devtools_client_(devtools_client),
+ virtual_time_controller_(virtual_time_controller),
+ animation_task_(base::MakeUnique<AnimationBeginFrameTask>(this)),
+ animation_begin_frame_interval_(animation_begin_frame_interval),
+ wait_for_compositor_ready_begin_frame_delay_(
+ wait_for_compositor_ready_begin_frame_delay),
+ weak_ptr_factory_(this) {
+ devtools_client_->GetHeadlessExperimental()->GetExperimental()->AddObserver(
+ this);
+ // No need to wait for completion of this, since we are waiting for the
+ // setNeedsBeginFramesChanged event instead, which will be sent at some point
+ // after enabling the domain.
+ devtools_client_->GetHeadlessExperimental()->GetExperimental()->Enable(
+ headless_experimental::EnableParams::Builder().Build());
+ virtual_time_controller_->ScheduleRepeatingTask(
+ animation_task_.get(), animation_begin_frame_interval_);
+}
+
+CompositorController::~CompositorController() {
+ virtual_time_controller_->CancelRepeatingTask(animation_task_.get());
+ devtools_client_->GetHeadlessExperimental()
+ ->GetExperimental()
+ ->RemoveObserver(this);
+}
+
+void CompositorController::PostBeginFrame(
+ const base::Callback<void(std::unique_ptr<BeginFrameResult>)>&
+ begin_frame_complete_callback,
+ std::unique_ptr<ScreenshotParams> screenshot) {
+ // In certain nesting situations, we should not issue a BeginFrame immediately
+ // - for example, issuing a new BeginFrame within a BeginFrameCompleted or
+ // NeedsBeginFramesChanged event can upset the compositor. We avoid these
+ // situations by issuing our BeginFrames from a separately posted task.
+ task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&CompositorController::BeginFrame,
+ weak_ptr_factory_.GetWeakPtr(), begin_frame_complete_callback,
+ base::Passed(&screenshot)));
+}
+
+void CompositorController::BeginFrame(
+ const base::Callback<void(std::unique_ptr<BeginFrameResult>)>&
+ begin_frame_complete_callback,
+ std::unique_ptr<ScreenshotParams> screenshot) {
+ DCHECK(!begin_frame_complete_callback_);
+ begin_frame_complete_callback_ = begin_frame_complete_callback;
+ if (needs_begin_frames_ || screenshot) {
+ auto params_builder = headless_experimental::BeginFrameParams::Builder();
+
+ // Use virtual time for frame time, so that rendering of animations etc. is
+ // aligned with virtual time progression.
+ base::Time frame_time = virtual_time_controller_->GetCurrentVirtualTime();
+ if (frame_time <= last_begin_frame_time_) {
+ // Frame time cannot go backwards or stop, so we issue another BeginFrame
+ // with a small time offset from the last BeginFrame's time instead.
+ frame_time =
+ last_begin_frame_time_ + base::TimeDelta::FromMicroseconds(1);
+ }
+ params_builder.SetFrameTime(frame_time.ToJsTime());
+ DCHECK_GT(frame_time, last_begin_frame_time_);
+ DCHECK_GT(frame_time.ToJsTime(), last_begin_frame_time_.ToJsTime());
+ last_begin_frame_time_ = frame_time;
+
+ params_builder.SetInterval(
+ animation_begin_frame_interval_.InMillisecondsF());
+
+ // TODO(eseckler): Set time fields. This requires obtaining the absolute
+ // virtual time stamp.
+ if (screenshot)
+ params_builder.SetScreenshot(std::move(screenshot));
+
+ devtools_client_->GetHeadlessExperimental()->GetExperimental()->BeginFrame(
+ params_builder.Build(),
+ base::Bind(&CompositorController::BeginFrameComplete,
+ weak_ptr_factory_.GetWeakPtr()));
+ } else {
+ BeginFrameComplete(nullptr);
+ }
+}
+
+void CompositorController::BeginFrameComplete(
+ std::unique_ptr<BeginFrameResult> result) {
+ DCHECK(begin_frame_complete_callback_);
+ auto callback = begin_frame_complete_callback_;
+ begin_frame_complete_callback_.Reset();
+ callback.Run(std::move(result));
+ if (idle_callback_) {
+ auto idle_callback = idle_callback_;
+ idle_callback_.Reset();
+ idle_callback.Run();
+ }
+}
+
+void CompositorController::OnNeedsBeginFramesChanged(
+ const NeedsBeginFramesChangedParams& params) {
+ needs_begin_frames_ = params.GetNeedsBeginFrames();
+
+ // If needs_begin_frames_ became true again and we're waiting for the
+ // compositor or a main frame update, continue posting BeginFrames - provided
+ // there's none outstanding.
+ if (compositor_ready_callback_ && needs_begin_frames_ &&
+ !begin_frame_complete_callback_ &&
+ wait_for_compositor_ready_begin_frame_task_.IsCancelled()) {
+ PostBeginFrame(base::Bind(
+ &CompositorController::WaitForCompositorReadyBeginFrameComplete,
+ weak_ptr_factory_.GetWeakPtr()));
+ } else if (main_frame_content_updated_callback_ && needs_begin_frames_ &&
+ !begin_frame_complete_callback_) {
+ PostWaitForMainFrameContentUpdateBeginFrame();
+ }
+}
+
+void CompositorController::OnMainFrameReadyForScreenshots(
+ const MainFrameReadyForScreenshotsParams& params) {
+ main_frame_ready_ = true;
+
+ // If a WaitForCompositorReadyBeginFrame is still scheduled, skip it.
+ if (!wait_for_compositor_ready_begin_frame_task_.IsCancelled()) {
+ wait_for_compositor_ready_begin_frame_task_.Cancel();
+ auto callback = compositor_ready_callback_;
+ compositor_ready_callback_.Reset();
+ callback.Run();
+ }
+}
+
+void CompositorController::WaitForCompositorReady(
+ const base::Closure& compositor_ready_callback) {
+ // We need to wait for the mainFrameReadyForScreenshots event, which will be
+ // issued once the renderer has submitted its first CompositorFrame in
+ // response to a BeginFrame. At that point, we know that the renderer
+ // compositor has initialized. We do this by issuing BeginFrames until we
+ // receive the event. To avoid bogging down the system with a flood of
+ // BeginFrames, we add a short delay between them.
+ // TODO(eseckler): Investigate if we can remove the need for these initial
+ // BeginFrames and the mainFrameReadyForScreenshots event, by making the
+ // compositor wait for the renderer in the very first BeginFrame, even if it
+ // isn't yet present in the surface hierarchy. Maybe surface synchronization
+ // can help here?
+ DCHECK(!begin_frame_complete_callback_);
+ DCHECK(!compositor_ready_callback_);
+
+ if (main_frame_ready_) {
+ compositor_ready_callback.Run();
+ return;
+ }
+
+ compositor_ready_callback_ = compositor_ready_callback;
+ if (needs_begin_frames_) {
+ // Post BeginFrames with a delay until the main frame becomes ready.
+ PostWaitForCompositorReadyBeginFrameTask();
+ }
+}
+
+void CompositorController::PostWaitForCompositorReadyBeginFrameTask() {
+ TRACE_EVENT0(
+ "headless",
+ "CompositorController::PostWaitForCompositorReadyBeginFrameTask");
+ wait_for_compositor_ready_begin_frame_task_.Reset(
+ base::Bind(&CompositorController::IssueWaitForCompositorReadyBeginFrame,
+ weak_ptr_factory_.GetWeakPtr()));
+
+ // We may receive the mainFrameReadyForScreenshots event before this task
+ // is run. In that case, we cancel it in OnMainFrameReadyForScreenshots to
+ // avoid another unnecessary BeginFrame.
+ task_runner_->PostDelayedTask(
+ FROM_HERE, wait_for_compositor_ready_begin_frame_task_.callback(),
+ wait_for_compositor_ready_begin_frame_delay_);
+}
+
+void CompositorController::IssueWaitForCompositorReadyBeginFrame() {
+ TRACE_EVENT0("headless",
+ "CompositorController::IssueWaitForCompositorReadyBeginFrame");
+ // No need for PostBeginFrame, since
+ // wait_for_compositor_ready_begin_frame_task_ has already been posted.
+ wait_for_compositor_ready_begin_frame_task_.Cancel();
+ BeginFrame(base::Bind(
+ &CompositorController::WaitForCompositorReadyBeginFrameComplete,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void CompositorController::WaitForCompositorReadyBeginFrameComplete(
+ std::unique_ptr<BeginFrameResult>) {
+ TRACE_EVENT0(
+ "headless",
+ "CompositorController::WaitForCompositorReadyBeginFrameComplete");
+ DCHECK(compositor_ready_callback_);
+
+ if (main_frame_ready_) {
+ auto callback = compositor_ready_callback_;
+ compositor_ready_callback_.Reset();
+ callback.Run();
+ return;
+ }
+
+ // Continue posting more BeginFrames with a delay until the main frame
+ // becomes ready. If needs_begin_frames_ is false, it will eventually turn
+ // true again once the renderer's compositor has started up.
+ if (needs_begin_frames_)
+ PostWaitForCompositorReadyBeginFrameTask();
+}
+
+void CompositorController::WaitUntilIdle(const base::Closure& idle_callback) {
+ TRACE_EVENT_INSTANT1("headless", "CompositorController::WaitUntilIdle",
+ TRACE_EVENT_SCOPE_THREAD, "begin_frame_in_flight",
+ !!begin_frame_complete_callback_);
+ DCHECK(!idle_callback_);
+
+ if (!begin_frame_complete_callback_) {
+ idle_callback.Run();
+ return;
+ }
+
+ idle_callback_ = idle_callback;
+}
+
+void CompositorController::WaitForMainFrameContentUpdate(
+ const base::Closure& main_frame_content_updated_callback) {
+ TRACE_EVENT0("headless",
+ "CompositorController::WaitForMainFrameContentUpdate");
+ DCHECK(!begin_frame_complete_callback_);
+ DCHECK(!main_frame_content_updated_callback_);
+ main_frame_content_updated_callback_ = main_frame_content_updated_callback;
+
+ // Post BeginFrames until we see a main frame update.
+ if (needs_begin_frames_)
+ PostWaitForMainFrameContentUpdateBeginFrame();
+}
+
+void CompositorController::PostWaitForMainFrameContentUpdateBeginFrame() {
+ TRACE_EVENT0(
+ "headless",
+ "CompositorController::PostWaitForMainFrameContentUpdateBeginFrame");
+ DCHECK(main_frame_content_updated_callback_);
+ PostBeginFrame(base::Bind(
+ &CompositorController::WaitForMainFrameContentUpdateBeginFrameComplete,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void CompositorController::WaitForMainFrameContentUpdateBeginFrameComplete(
+ std::unique_ptr<BeginFrameResult> result) {
+ if (!result)
+ return;
+
+ TRACE_EVENT1(
+ "headless",
+ "CompositorController::WaitForMainFrameContentUpdateBeginFrameComplete",
+ "main_frame_content_updated", result->GetMainFrameContentUpdated());
+ DCHECK(!begin_frame_complete_callback_);
+ DCHECK(main_frame_content_updated_callback_);
+
+ if (result->GetMainFrameContentUpdated()) {
+ auto callback = main_frame_content_updated_callback_;
+ main_frame_content_updated_callback_.Reset();
+ callback.Run();
+ return;
+ }
+
+ // Continue posting BeginFrames until we see a main frame update.
+ if (needs_begin_frames_)
+ PostWaitForMainFrameContentUpdateBeginFrame();
+}
+
+void CompositorController::CaptureScreenshot(
+ ScreenshotParamsFormat format,
+ int quality,
+ const base::Callback<void(const std::string&)>&
+ screenshot_captured_callback) {
+ TRACE_EVENT0("headless", "CompositorController::CaptureScreenshot");
+ DCHECK(!begin_frame_complete_callback_);
+ DCHECK(!screenshot_captured_callback_);
+ DCHECK(main_frame_ready_);
+
+ screenshot_captured_callback_ = screenshot_captured_callback;
+
+ // Let AnimationBeginFrameTask know that it doesn't need to issue an
+ // animation BeginFrame for the current virtual time pause.
+ animation_task_->CompositorControllerIssuingScreenshotBeginFrame();
+
+ PostBeginFrame(
+ base::Bind(&CompositorController::CaptureScreenshotBeginFrameComplete,
+ weak_ptr_factory_.GetWeakPtr()),
+ ScreenshotParams::Builder()
+ .SetFormat(format)
+ .SetQuality(quality)
+ .Build());
+}
+
+void CompositorController::CaptureScreenshotBeginFrameComplete(
+ std::unique_ptr<BeginFrameResult> result) {
+ TRACE_EVENT1(
+ "headless", "CompositorController::CaptureScreenshotBeginFrameComplete",
+ "hasScreenshotData",
+ result ? std::to_string(result->HasScreenshotData()) : "invalid");
+ DCHECK(screenshot_captured_callback_);
+ if (result && result->HasScreenshotData()) {
+ // TODO(eseckler): Look into returning binary screenshot data via DevTools.
+ std::string decoded_data;
+ base::Base64Decode(result->GetScreenshotData(), &decoded_data);
+ auto callback = screenshot_captured_callback_;
+ screenshot_captured_callback_.Reset();
+ callback.Run(decoded_data);
+ } else {
+ LOG(ERROR) << "Screenshotting failed, BeginFrameResult has no data and "
+ "hasDamage is "
+ << (result ? std::to_string(result->HasScreenshotData())
+ : "invalid");
+ auto callback = screenshot_captured_callback_;
+ screenshot_captured_callback_.Reset();
+ callback.Run(std::string());
+ }
+}
+
+} // namespace headless
diff --git a/chromium/headless/public/util/compositor_controller.h b/chromium/headless/public/util/compositor_controller.h
new file mode 100644
index 00000000000..09012a4b377
--- /dev/null
+++ b/chromium/headless/public/util/compositor_controller.h
@@ -0,0 +1,138 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef HEADLESS_PUBLIC_UTIL_COMPOSITOR_CONTROLLER_H_
+#define HEADLESS_PUBLIC_UTIL_COMPOSITOR_CONTROLLER_H_
+
+#include "base/callback.h"
+#include "base/cancelable_callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequenced_task_runner.h"
+#include "base/time/time.h"
+#include "headless/public/devtools/domains/headless_experimental.h"
+#include "headless/public/headless_devtools_client.h"
+
+namespace headless {
+
+class VirtualTimeController;
+
+// Issues BeginFrames (Chromium's vsync signal) while virtual time advances and
+// and takes screenshots.
+class HEADLESS_EXPORT CompositorController
+ : public headless_experimental::ExperimentalObserver {
+ public:
+ using BeginFrameResult = headless_experimental::BeginFrameResult;
+ using MainFrameReadyForScreenshotsParams =
+ headless_experimental::MainFrameReadyForScreenshotsParams;
+ using NeedsBeginFramesChangedParams =
+ headless_experimental::NeedsBeginFramesChangedParams;
+ using ScreenshotParams = headless_experimental::ScreenshotParams;
+ using ScreenshotParamsFormat = headless_experimental::ScreenshotParamsFormat;
+
+ // |animation_begin_frame_interval| specifies the virtual time between
+ // individual BeginFrames while virtual time advances.
+ // |wait_for_compositor_ready_begin_frame_delay| is the real time delay
+ // between BeginFrames that are sent while waiting for the main frame
+ // compositor to become ready (real time).
+ CompositorController(
+ scoped_refptr<base::SequencedTaskRunner> task_runner,
+ HeadlessDevToolsClient* devtools_client,
+ VirtualTimeController* virtual_time_controller,
+ base::TimeDelta animation_begin_frame_interval,
+ base::TimeDelta wait_for_compositor_ready_begin_frame_delay);
+ ~CompositorController() override;
+
+ // Issues BeginFrames until the main frame's compositor has completed
+ // initialization. Should not be called again until
+ // |compositor_ready_callback| was run. Should only be called while no other
+ // BeginFrame is in flight.
+ void WaitForCompositorReady(const base::Closure& compositor_ready_callback);
+
+ // Executes |idle_callback| when no BeginFrames are in flight.
+ void WaitUntilIdle(const base::Closure& idle_callback);
+
+ // Issues BeginFrames until a new main frame update was committed. Should not
+ // be called again until |main_frame_content_updated_callback| was run. Should
+ // only be called while no other BeginFrame is in flight.
+ //
+ // This can be used in situations where e.g. the main frame size changes and
+ // we need to wait for the update to propagate down into a new surface before
+ // taking a screenshot.
+ // TODO(eseckler): Investigate whether we can replace this with surface
+ // synchronization or some other mechanism that avoids the need for additional
+ // BeginFrames.
+ void WaitForMainFrameContentUpdate(
+ const base::Closure& main_frame_content_updated_callback);
+
+ // Captures a screenshot by issuing a BeginFrame. |quality| is only valid for
+ // jpeg format screenshots, in range 0..100. Should not be called again until
+ // |screenshot_captured_callback| was run. Should only be called while no
+ // other BeginFrame is in flight and after the compositor is ready.
+ void CaptureScreenshot(ScreenshotParamsFormat format,
+ int quality,
+ const base::Callback<void(const std::string&)>&
+ screenshot_captured_callback);
+
+ private:
+ class AnimationBeginFrameTask;
+
+ // headless_experimental_::Observer implementation:
+ void OnNeedsBeginFramesChanged(
+ const NeedsBeginFramesChangedParams& params) override;
+ void OnMainFrameReadyForScreenshots(
+ const MainFrameReadyForScreenshotsParams& params) override;
+
+ // Posts a BeginFrame as a new task to avoid nesting it inside the current
+ // callstack, which can upset the compositor.
+ void PostBeginFrame(
+ const base::Callback<void(std::unique_ptr<BeginFrameResult>)>&
+ begin_frame_complete_callback,
+ std::unique_ptr<ScreenshotParams> screenshot = nullptr);
+ // Issues a BeginFrame synchronously and runs |begin_frame_complete_callback|
+ // when done. Should not be called again until |begin_frame_complete_callback|
+ // was run.
+ void BeginFrame(const base::Callback<void(std::unique_ptr<BeginFrameResult>)>&
+ begin_frame_complete_callback,
+ std::unique_ptr<ScreenshotParams> screenshot = nullptr);
+ // Runs the |begin_frame_complete_callback_| and the |idle_callback_| if set.
+ void BeginFrameComplete(std::unique_ptr<BeginFrameResult>);
+
+ // Posts a task to issue a BeginFrame while waiting for the
+ // mainFrameReadyForScreenshots event. The taks may be cancelled by the event.
+ void PostWaitForCompositorReadyBeginFrameTask();
+ void IssueWaitForCompositorReadyBeginFrame();
+ void WaitForCompositorReadyBeginFrameComplete(
+ std::unique_ptr<BeginFrameResult>);
+
+ // Posts a BeginFrame while waiting for a main frame content update.
+ void PostWaitForMainFrameContentUpdateBeginFrame();
+ void WaitForMainFrameContentUpdateBeginFrameComplete(
+ std::unique_ptr<BeginFrameResult> result);
+
+ void CaptureScreenshotBeginFrameComplete(
+ std::unique_ptr<BeginFrameResult> result);
+
+ scoped_refptr<base::SequencedTaskRunner> task_runner_;
+ HeadlessDevToolsClient* devtools_client_; // NOT OWNED
+ VirtualTimeController* virtual_time_controller_; // NOT OWNED
+ std::unique_ptr<AnimationBeginFrameTask> animation_task_;
+ base::Closure compositor_ready_callback_;
+ base::Closure idle_callback_;
+ base::Closure main_frame_content_updated_callback_;
+ base::Callback<void(const std::string&)> screenshot_captured_callback_;
+ base::Callback<void(std::unique_ptr<BeginFrameResult>)>
+ begin_frame_complete_callback_;
+ base::CancelableClosure wait_for_compositor_ready_begin_frame_task_;
+ base::TimeDelta animation_begin_frame_interval_;
+ base::TimeDelta wait_for_compositor_ready_begin_frame_delay_;
+ bool needs_begin_frames_ = false;
+ bool main_frame_ready_ = false;
+ base::Time last_begin_frame_time_ = base::Time::UnixEpoch();
+ base::WeakPtrFactory<CompositorController> weak_ptr_factory_;
+};
+
+} // namespace headless
+
+#endif // HEADLESS_PUBLIC_UTIL_COMPOSITOR_CONTROLLER_H_
diff --git a/chromium/headless/public/util/compositor_controller_browsertest.cc b/chromium/headless/public/util/compositor_controller_browsertest.cc
new file mode 100644
index 00000000000..5169490626e
--- /dev/null
+++ b/chromium/headless/public/util/compositor_controller_browsertest.cc
@@ -0,0 +1,110 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "headless/public/util/compositor_controller.h"
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "build/build_config.h"
+#include "cc/base/switches.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/test/browser_test.h"
+#include "headless/public/devtools/domains/runtime.h"
+#include "headless/public/headless_browser.h"
+#include "headless/public/headless_devtools_client.h"
+#include "headless/public/util/virtual_time_controller.h"
+#include "headless/test/headless_browser_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace headless {
+
+// BeginFrameControl is not supported on Mac.
+#if !defined(OS_MACOSX)
+
+namespace {
+static constexpr base::TimeDelta kAnimationFrameInterval =
+ base::TimeDelta::FromMilliseconds(16);
+static constexpr base::TimeDelta kWaitForCompositorReadyFrameDelay =
+ base::TimeDelta::FromMilliseconds(20);
+} // namespace
+
+class CompositorControllerBrowserTest
+ : public HeadlessAsyncDevTooledBrowserTest {
+ public:
+ void SetUpCommandLine(base::CommandLine* command_line) override {
+ HeadlessAsyncDevTooledBrowserTest::SetUpCommandLine(command_line);
+ command_line->AppendSwitch(cc::switches::kRunAllCompositorStagesBeforeDraw);
+ command_line->AppendSwitch(switches::kDisableNewContentRenderingTimeout);
+ }
+
+ bool GetEnableBeginFrameControl() override { return true; }
+
+ void RunDevTooledTest() override {
+ virtual_time_controller_ =
+ base::MakeUnique<VirtualTimeController>(devtools_client_.get());
+ compositor_controller_ = base::MakeUnique<CompositorController>(
+ browser()->BrowserMainThread(), devtools_client_.get(),
+ virtual_time_controller_.get(), kAnimationFrameInterval,
+ kWaitForCompositorReadyFrameDelay);
+
+ compositor_controller_->WaitForCompositorReady(
+ base::Bind(&CompositorControllerBrowserTest::OnCompositorReady,
+ base::Unretained(this)));
+ }
+
+ void OnCompositorReady() {
+ // Request animation frames in the main frame.
+ devtools_client_->GetRuntime()->Evaluate(
+ "window.rafCount = 0;"
+ "function onRaf(timestamp) {"
+ " window.rafCount++;"
+ " window.requestAnimationFrame(onRaf);"
+ "};"
+ "window.requestAnimationFrame(onRaf);",
+ base::Bind(&CompositorControllerBrowserTest::OnRafReady,
+ base::Unretained(this)));
+ }
+
+ void OnRafReady(std::unique_ptr<runtime::EvaluateResult> result) {
+ EXPECT_NE(nullptr, result);
+ EXPECT_FALSE(result->HasExceptionDetails());
+
+ virtual_time_controller_->GrantVirtualTimeBudget(
+ emulation::VirtualTimePolicy::ADVANCE,
+ kNumFrames * kAnimationFrameInterval, base::Bind([]() {}),
+ base::Bind(&CompositorControllerBrowserTest::OnVirtualTimeBudgetExpired,
+ base::Unretained(this)));
+ }
+
+ void OnVirtualTimeBudgetExpired() {
+ // Get animation frame count.
+ devtools_client_->GetRuntime()->Evaluate(
+ "window.rafCount",
+ base::Bind(&CompositorControllerBrowserTest::OnGetRafCount,
+ base::Unretained(this)));
+ }
+
+ void OnGetRafCount(std::unique_ptr<runtime::EvaluateResult> result) {
+ EXPECT_NE(nullptr, result);
+ EXPECT_FALSE(result->HasExceptionDetails());
+
+ EXPECT_EQ(kNumFrames, result->GetResult()->GetValue()->GetInt());
+ FinishAsynchronousTest();
+ }
+
+ private:
+ static constexpr int kNumFrames = 3;
+
+ std::unique_ptr<VirtualTimeController> virtual_time_controller_;
+ std::unique_ptr<CompositorController> compositor_controller_;
+};
+
+/* static */
+constexpr int CompositorControllerBrowserTest::kNumFrames;
+
+HEADLESS_ASYNC_DEVTOOLED_TEST_F(CompositorControllerBrowserTest);
+
+#endif // !defined(OS_MACOSX)
+
+} // namespace headless
diff --git a/chromium/headless/public/util/compositor_controller_unittest.cc b/chromium/headless/public/util/compositor_controller_unittest.cc
new file mode 100644
index 00000000000..33aa032de0c
--- /dev/null
+++ b/chromium/headless/public/util/compositor_controller_unittest.cc
@@ -0,0 +1,511 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "headless/public/util/compositor_controller.h"
+
+#include "base/base64.h"
+#include "base/bind.h"
+#include "base/json/json_writer.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/test_simple_task_runner.h"
+#include "headless/public/internal/headless_devtools_client_impl.h"
+#include "headless/public/util/testing/mock_devtools_agent_host.h"
+#include "headless/public/util/virtual_time_controller.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace headless {
+
+namespace {
+static constexpr base::TimeDelta kAnimationFrameInterval =
+ base::TimeDelta::FromMilliseconds(16);
+static constexpr base::TimeDelta kWaitForCompositorReadyFrameDelay =
+ base::TimeDelta::FromMilliseconds(20);
+} // namespace
+
+using testing::Return;
+using testing::_;
+
+class TestVirtualTimeController : public VirtualTimeController {
+ public:
+ TestVirtualTimeController(HeadlessDevToolsClient* devtools_client)
+ : VirtualTimeController(devtools_client) {}
+ ~TestVirtualTimeController() override = default;
+
+ MOCK_METHOD4(GrantVirtualTimeBudget,
+ void(emulation::VirtualTimePolicy policy,
+ base::TimeDelta budget,
+ const base::Callback<void()>& set_up_complete_callback,
+ const base::Callback<void()>& budget_expired_callback));
+ MOCK_METHOD2(ScheduleRepeatingTask,
+ void(RepeatingTask* task, base::TimeDelta interval));
+ MOCK_METHOD1(CancelRepeatingTask, void(RepeatingTask* task));
+
+ MOCK_CONST_METHOD0(GetVirtualTimeBase, base::Time());
+ MOCK_CONST_METHOD0(GetCurrentVirtualTimeOffset, base::TimeDelta());
+};
+
+class CompositorControllerTest : public ::testing::Test {
+ protected:
+ CompositorControllerTest() {
+ task_runner_ = base::MakeRefCounted<base::TestSimpleTaskRunner>();
+ client_.SetTaskRunnerForTests(task_runner_);
+ mock_host_ = base::MakeRefCounted<MockDevToolsAgentHost>();
+
+ EXPECT_CALL(*mock_host_, IsAttached()).WillOnce(Return(false));
+ EXPECT_CALL(*mock_host_, AttachClient(&client_));
+ client_.AttachToHost(mock_host_.get());
+ virtual_time_controller_ =
+ base::MakeUnique<TestVirtualTimeController>(&client_);
+ EXPECT_CALL(*virtual_time_controller_,
+ ScheduleRepeatingTask(_, kAnimationFrameInterval))
+ .WillOnce(testing::SaveArg<0>(&task_));
+ ExpectHeadlessExperimentalEnable();
+ controller_ = base::MakeUnique<CompositorController>(
+ task_runner_, &client_, virtual_time_controller_.get(),
+ kAnimationFrameInterval, kWaitForCompositorReadyFrameDelay);
+ EXPECT_NE(nullptr, task_);
+ }
+
+ ~CompositorControllerTest() override {
+ EXPECT_CALL(*virtual_time_controller_, CancelRepeatingTask(_));
+ }
+
+ void ExpectHeadlessExperimentalEnable() {
+ last_command_id_ += 2;
+ EXPECT_CALL(*mock_host_,
+ DispatchProtocolMessage(
+ &client_,
+ base::StringPrintf(
+ "{\"id\":%d,\"method\":\"HeadlessExperimental.enable\","
+ "\"params\":{}}",
+ last_command_id_)))
+ .WillOnce(Return(true));
+ }
+
+ void ExpectVirtualTime(double base, double offset) {
+ auto base_time = base::Time::FromJsTime(base);
+ auto offset_delta = base::TimeDelta::FromMilliseconds(offset);
+ EXPECT_CALL(*virtual_time_controller_, GetVirtualTimeBase())
+ .WillOnce(Return(base_time));
+ EXPECT_CALL(*virtual_time_controller_, GetCurrentVirtualTimeOffset())
+ .WillOnce(Return(offset_delta));
+
+ // Next BeginFrame's time should be the virtual time provided it has
+ // progressed.
+ base::Time virtual_time = base_time + offset_delta;
+ if (virtual_time > next_begin_frame_time_)
+ next_begin_frame_time_ = virtual_time;
+ }
+
+ void ExpectBeginFrame(std::unique_ptr<headless_experimental::ScreenshotParams>
+ screenshot_params = nullptr) {
+ last_command_id_ += 2;
+ base::DictionaryValue params;
+ auto builder =
+ std::move(headless_experimental::BeginFrameParams::Builder()
+ .SetFrameTime(next_begin_frame_time_.ToJsTime())
+ .SetInterval(kAnimationFrameInterval.InMillisecondsF()));
+ if (screenshot_params)
+ builder.SetScreenshot(std::move(screenshot_params));
+ // Subsequent BeginFrames should have a later timestamp.
+ next_begin_frame_time_ += base::TimeDelta::FromMicroseconds(1);
+
+ std::string params_json;
+ auto params_value = builder.Build()->Serialize();
+ base::JSONWriter::Write(*params_value, &params_json);
+
+ EXPECT_CALL(
+ *mock_host_,
+ DispatchProtocolMessage(
+ &client_,
+ base::StringPrintf(
+ "{\"id\":%d,\"method\":\"HeadlessExperimental.beginFrame\","
+ "\"params\":%s}",
+ last_command_id_, params_json.c_str())))
+ .WillOnce(Return(true));
+ }
+
+ void SendBeginFrameReply(bool has_damage,
+ bool main_frame_content_updated,
+ const std::string& screenshot_data) {
+ auto result = headless_experimental::BeginFrameResult::Builder()
+ .SetHasDamage(has_damage)
+ .SetMainFrameContentUpdated(main_frame_content_updated)
+ .Build();
+ if (screenshot_data.length())
+ result->SetScreenshotData(screenshot_data);
+ std::string result_json;
+ auto result_value = result->Serialize();
+ base::JSONWriter::Write(*result_value, &result_json);
+
+ client_.DispatchProtocolMessage(
+ mock_host_.get(),
+ base::StringPrintf("{\"id\":%d,\"result\":%s}", last_command_id_,
+ result_json.c_str()));
+ }
+
+ void SendNeedsBeginFramesEvent(bool needs_begin_frames) {
+ client_.DispatchProtocolMessage(
+ mock_host_.get(),
+ base::StringPrintf("{\"method\":\"HeadlessExperimental."
+ "needsBeginFramesChanged\",\"params\":{"
+ "\"needsBeginFrames\":%s}}",
+ needs_begin_frames ? "true" : "false"));
+ // Events are dispatched asynchronously.
+ task_runner_->RunPendingTasks();
+ }
+
+ void SendMainFrameReadyForScreenshotsEvent() {
+ client_.DispatchProtocolMessage(mock_host_.get(),
+ "{\"method\":\"HeadlessExperimental."
+ "mainFrameReadyForScreenshots\",\"params\":"
+ "{}}");
+ // Events are dispatched asynchronously.
+ task_runner_->RunPendingTasks();
+ }
+
+ scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+ scoped_refptr<MockDevToolsAgentHost> mock_host_;
+ HeadlessDevToolsClientImpl client_;
+ std::unique_ptr<TestVirtualTimeController> virtual_time_controller_;
+ std::unique_ptr<CompositorController> controller_;
+ int last_command_id_ = -2;
+ TestVirtualTimeController::RepeatingTask* task_ = nullptr;
+ base::Time next_begin_frame_time_ =
+ base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(1);
+};
+
+TEST_F(CompositorControllerTest, WaitForCompositorReady) {
+ // Shouldn't send any commands yet as no needsBeginFrames event was sent yet.
+ bool ready = false;
+ controller_->WaitForCompositorReady(
+ base::Bind([](bool* ready) { *ready = true; }, &ready));
+ EXPECT_FALSE(ready);
+
+ // Sends BeginFrames with delay while they are needed.
+ SendNeedsBeginFramesEvent(true);
+ EXPECT_TRUE(task_runner_->HasPendingTask());
+ ExpectVirtualTime(0, 0);
+ ExpectBeginFrame();
+ task_runner_->RunPendingTasks();
+
+ SendBeginFrameReply(true, false, std::string());
+
+ EXPECT_TRUE(task_runner_->HasPendingTask());
+ EXPECT_EQ(kWaitForCompositorReadyFrameDelay,
+ task_runner_->NextPendingTaskDelay());
+ ExpectVirtualTime(0, 0);
+ ExpectBeginFrame();
+ task_runner_->RunPendingTasks();
+
+ EXPECT_FALSE(task_runner_->HasPendingTask());
+
+ SendBeginFrameReply(false, false, std::string());
+
+ EXPECT_TRUE(task_runner_->HasPendingTask());
+ EXPECT_EQ(kWaitForCompositorReadyFrameDelay,
+ task_runner_->NextPendingTaskDelay());
+ ExpectVirtualTime(0, 0);
+ ExpectBeginFrame();
+ task_runner_->RunPendingTasks();
+
+ // No new BeginFrames are scheduled when BeginFrames are not needed.
+ SendNeedsBeginFramesEvent(false);
+ EXPECT_FALSE(task_runner_->HasPendingTask());
+
+ SendBeginFrameReply(false, false, std::string());
+ EXPECT_FALSE(task_runner_->HasPendingTask());
+
+ // Restarts sending BeginFrames when they are needed again.
+ SendNeedsBeginFramesEvent(true);
+ EXPECT_TRUE(task_runner_->HasPendingTask());
+ ExpectVirtualTime(0, 0);
+ ExpectBeginFrame();
+ task_runner_->RunPendingTasks();
+
+ // Stops sending BeginFrames when main frame becomes ready.
+ SendMainFrameReadyForScreenshotsEvent();
+ EXPECT_FALSE(task_runner_->HasPendingTask());
+
+ EXPECT_FALSE(ready);
+ SendBeginFrameReply(true, true, std::string());
+ EXPECT_FALSE(task_runner_->HasPendingTask());
+ EXPECT_TRUE(ready);
+}
+
+TEST_F(CompositorControllerTest, CaptureScreenshot) {
+ SendMainFrameReadyForScreenshotsEvent();
+ EXPECT_FALSE(task_runner_->HasPendingTask());
+
+ bool done = false;
+ controller_->CaptureScreenshot(
+ headless_experimental::ScreenshotParamsFormat::PNG, 100,
+ base::Bind(
+ [](bool* done, const std::string& screenshot_data) {
+ *done = true;
+ EXPECT_EQ("test", screenshot_data);
+ },
+ &done));
+
+ EXPECT_TRUE(task_runner_->HasPendingTask());
+ ExpectVirtualTime(0, 0);
+ ExpectBeginFrame(
+ headless_experimental::ScreenshotParams::Builder()
+ .SetFormat(headless_experimental::ScreenshotParamsFormat::PNG)
+ .SetQuality(100)
+ .Build());
+ task_runner_->RunPendingTasks();
+
+ std::string base64;
+ base::Base64Encode("test", &base64);
+ SendBeginFrameReply(true, true, base64);
+ EXPECT_FALSE(task_runner_->HasPendingTask());
+ EXPECT_TRUE(done);
+}
+
+TEST_F(CompositorControllerTest, WaitForMainFrameContentUpdate) {
+ bool updated = false;
+ controller_->WaitForMainFrameContentUpdate(
+ base::Bind([](bool* updated) { *updated = true; }, &updated));
+ EXPECT_FALSE(updated);
+
+ // Sends BeginFrames while they are needed.
+ SendNeedsBeginFramesEvent(true);
+ EXPECT_TRUE(task_runner_->HasPendingTask());
+ ExpectVirtualTime(0, 0);
+ ExpectBeginFrame();
+ task_runner_->RunPendingTasks();
+
+ SendBeginFrameReply(true, false, std::string());
+
+ EXPECT_TRUE(task_runner_->HasPendingTask());
+ EXPECT_EQ(base::TimeDelta(), task_runner_->NextPendingTaskDelay());
+ ExpectVirtualTime(0, 0);
+ ExpectBeginFrame();
+ task_runner_->RunPendingTasks();
+
+ EXPECT_FALSE(task_runner_->HasPendingTask());
+
+ SendBeginFrameReply(false, false, std::string());
+
+ EXPECT_TRUE(task_runner_->HasPendingTask());
+ EXPECT_EQ(base::TimeDelta(), task_runner_->NextPendingTaskDelay());
+ ExpectVirtualTime(0, 0);
+ ExpectBeginFrame();
+ task_runner_->RunPendingTasks();
+
+ // No new BeginFrames are scheduled when BeginFrames are not needed.
+ SendNeedsBeginFramesEvent(false);
+ EXPECT_FALSE(task_runner_->HasPendingTask());
+
+ SendBeginFrameReply(false, false, std::string());
+ EXPECT_FALSE(task_runner_->HasPendingTask());
+
+ // Restarts sending BeginFrames when they are needed again.
+ SendNeedsBeginFramesEvent(true);
+ EXPECT_TRUE(task_runner_->HasPendingTask());
+ ExpectVirtualTime(0, 0);
+ ExpectBeginFrame();
+ task_runner_->RunPendingTasks();
+
+ // Stops sending BeginFrames when an main frame update is included.
+ SendMainFrameReadyForScreenshotsEvent();
+ EXPECT_FALSE(task_runner_->HasPendingTask());
+
+ EXPECT_FALSE(updated);
+ SendBeginFrameReply(true, true, std::string());
+ EXPECT_FALSE(task_runner_->HasPendingTask());
+ EXPECT_TRUE(updated);
+}
+
+TEST_F(CompositorControllerTest, SendsAnimationFrames) {
+ bool can_continue = false;
+ auto continue_callback = base::Bind(
+ [](bool* can_continue) { *can_continue = true; }, &can_continue);
+
+ // Task doesn't block virtual time request.
+ task_->BudgetRequested(base::TimeDelta(),
+ base::TimeDelta::FromMilliseconds(100),
+ continue_callback);
+ EXPECT_TRUE(can_continue);
+ can_continue = false;
+
+ // Doesn't send BeginFrames before virtual time started.
+ SendNeedsBeginFramesEvent(true);
+ EXPECT_FALSE(task_runner_->HasPendingTask());
+
+ // Sends a BeginFrame after interval elapsed.
+ task_->IntervalElapsed(kAnimationFrameInterval, continue_callback);
+ EXPECT_FALSE(can_continue);
+
+ EXPECT_TRUE(task_runner_->HasPendingTask());
+ ExpectVirtualTime(1000, kAnimationFrameInterval.InMillisecondsF());
+ ExpectBeginFrame();
+ task_runner_->RunPendingTasks();
+
+ // Lets virtual time continue after BeginFrame was completed.
+ SendBeginFrameReply(false, false, std::string());
+ EXPECT_FALSE(task_runner_->HasPendingTask());
+ EXPECT_TRUE(can_continue);
+ can_continue = false;
+
+ // Sends another BeginFrame after next interval elapsed.
+ task_->IntervalElapsed(kAnimationFrameInterval, continue_callback);
+ EXPECT_FALSE(can_continue);
+
+ EXPECT_TRUE(task_runner_->HasPendingTask());
+ ExpectVirtualTime(1000, kAnimationFrameInterval.InMillisecondsF() * 2);
+ ExpectBeginFrame();
+ task_runner_->RunPendingTasks();
+
+ // Lets virtual time continue after BeginFrame was completed.
+ SendBeginFrameReply(false, false, std::string());
+ EXPECT_FALSE(task_runner_->HasPendingTask());
+ EXPECT_TRUE(can_continue);
+ can_continue = false;
+}
+
+TEST_F(CompositorControllerTest, SkipsAnimationFrameForScreenshots) {
+ bool can_continue = false;
+ auto continue_callback = base::Bind(
+ [](bool* can_continue) { *can_continue = true; }, &can_continue);
+
+ SendMainFrameReadyForScreenshotsEvent();
+ EXPECT_FALSE(task_runner_->HasPendingTask());
+ SendNeedsBeginFramesEvent(true);
+ EXPECT_FALSE(task_runner_->HasPendingTask());
+
+ // Doesn't send a BeginFrame after interval elapsed if a screenshot is taken
+ // instead.
+ task_->IntervalElapsed(kAnimationFrameInterval, continue_callback);
+ EXPECT_FALSE(can_continue);
+
+ controller_->CaptureScreenshot(
+ headless_experimental::ScreenshotParamsFormat::PNG, 100,
+ base::Bind([](const std::string&) {}));
+
+ EXPECT_TRUE(task_runner_->HasPendingTask());
+ ExpectVirtualTime(0, 0);
+ ExpectBeginFrame(
+ headless_experimental::ScreenshotParams::Builder()
+ .SetFormat(headless_experimental::ScreenshotParamsFormat::PNG)
+ .SetQuality(100)
+ .Build());
+ task_runner_->RunPendingTasks();
+
+ EXPECT_TRUE(can_continue);
+ can_continue = false;
+}
+
+TEST_F(CompositorControllerTest,
+ PostponesAnimationFrameWhenBudgetExpired) {
+ bool can_continue = false;
+ auto continue_callback = base::Bind(
+ [](bool* can_continue) { *can_continue = true; }, &can_continue);
+
+ SendNeedsBeginFramesEvent(true);
+ EXPECT_FALSE(task_runner_->HasPendingTask());
+
+ // Doesn't send a BeginFrame after interval elapsed if the budget also
+ // expired.
+ task_->IntervalElapsed(kAnimationFrameInterval, continue_callback);
+ EXPECT_FALSE(can_continue);
+
+ task_->BudgetExpired(kAnimationFrameInterval);
+ EXPECT_TRUE(can_continue);
+ can_continue = false;
+ // Flush cancelled task.
+ task_runner_->RunPendingTasks();
+
+ // Sends a BeginFrame when more virtual time budget is requested.
+ ExpectVirtualTime(0, 0);
+ ExpectBeginFrame();
+ task_->BudgetRequested(kAnimationFrameInterval,
+ base::TimeDelta::FromMilliseconds(100),
+ continue_callback);
+ EXPECT_FALSE(can_continue);
+
+ SendBeginFrameReply(false, false, std::string());
+ EXPECT_FALSE(task_runner_->HasPendingTask());
+ EXPECT_TRUE(can_continue);
+ can_continue = false;
+}
+
+TEST_F(CompositorControllerTest,
+ SkipsAnimationFrameWhenBudgetExpiredAndScreenshotWasTaken) {
+ bool can_continue = false;
+ auto continue_callback = base::Bind(
+ [](bool* can_continue) { *can_continue = true; }, &can_continue);
+
+ SendMainFrameReadyForScreenshotsEvent();
+ EXPECT_FALSE(task_runner_->HasPendingTask());
+ SendNeedsBeginFramesEvent(true);
+ EXPECT_FALSE(task_runner_->HasPendingTask());
+
+ // Doesn't send a BeginFrame after interval elapsed if the budget also
+ // expired.
+ task_->IntervalElapsed(kAnimationFrameInterval, continue_callback);
+ EXPECT_FALSE(can_continue);
+
+ task_->BudgetExpired(kAnimationFrameInterval);
+ EXPECT_TRUE(can_continue);
+ can_continue = false;
+ // Flush cancelled task.
+ task_runner_->RunPendingTasks();
+
+ controller_->CaptureScreenshot(
+ headless_experimental::ScreenshotParamsFormat::PNG, 100,
+ base::Bind([](const std::string&) {}));
+
+ EXPECT_TRUE(task_runner_->HasPendingTask());
+ ExpectVirtualTime(0, 0);
+ ExpectBeginFrame(
+ headless_experimental::ScreenshotParams::Builder()
+ .SetFormat(headless_experimental::ScreenshotParamsFormat::PNG)
+ .SetQuality(100)
+ .Build());
+ task_runner_->RunPendingTasks();
+
+ // Sends a BeginFrame when more virtual time budget is requested.
+ task_->BudgetRequested(kAnimationFrameInterval,
+ base::TimeDelta::FromMilliseconds(100),
+ continue_callback);
+ EXPECT_TRUE(can_continue);
+ can_continue = false;
+}
+
+TEST_F(CompositorControllerTest, WaitUntilIdle) {
+ bool idle = false;
+ auto idle_callback = base::Bind([](bool* idle) { *idle = true; }, &idle);
+
+ SendNeedsBeginFramesEvent(true);
+ EXPECT_FALSE(task_runner_->HasPendingTask());
+
+ // WaitUntilIdle executes callback immediately if no BeginFrame is active.
+ controller_->WaitUntilIdle(idle_callback);
+ EXPECT_TRUE(idle);
+ idle = false;
+
+ // Send a BeginFrame.
+ task_->IntervalElapsed(kAnimationFrameInterval, base::Bind([]() {}));
+
+ EXPECT_TRUE(task_runner_->HasPendingTask());
+ ExpectVirtualTime(0, 0);
+ ExpectBeginFrame();
+ task_runner_->RunPendingTasks();
+
+ // WaitUntilIdle only executes callback after BeginFrame was completed.
+ controller_->WaitUntilIdle(idle_callback);
+ EXPECT_FALSE(idle);
+
+ SendBeginFrameReply(false, false, std::string());
+ EXPECT_FALSE(task_runner_->HasPendingTask());
+ EXPECT_TRUE(idle);
+ idle = false;
+}
+
+} // namespace headless
diff --git a/chromium/headless/public/util/deterministic_dispatcher.cc b/chromium/headless/public/util/deterministic_dispatcher.cc
index bfa75e5b714..e9507c53722 100644
--- a/chromium/headless/public/util/deterministic_dispatcher.cc
+++ b/chromium/headless/public/util/deterministic_dispatcher.cc
@@ -20,7 +20,7 @@ DeterministicDispatcher::DeterministicDispatcher(
navigation_in_progress_(false),
weak_ptr_factory_(this) {}
-DeterministicDispatcher::~DeterministicDispatcher() {}
+DeterministicDispatcher::~DeterministicDispatcher() = default;
void DeterministicDispatcher::JobCreated(ManagedDispatchURLRequestJob* job) {
base::AutoLock lock(lock_);
@@ -144,7 +144,7 @@ void DeterministicDispatcher::NavigationDoneTask() {
DeterministicDispatcher::Request::Request() = default;
DeterministicDispatcher::Request::Request(Request&&) = default;
-DeterministicDispatcher::Request::~Request() {}
+DeterministicDispatcher::Request::~Request() = default;
DeterministicDispatcher::Request::Request(
ManagedDispatchURLRequestJob* url_request)
diff --git a/chromium/headless/public/util/deterministic_dispatcher_test.cc b/chromium/headless/public/util/deterministic_dispatcher_test.cc
index 48b30e298c5..5b769800ec8 100644
--- a/chromium/headless/public/util/deterministic_dispatcher_test.cc
+++ b/chromium/headless/public/util/deterministic_dispatcher_test.cc
@@ -23,8 +23,8 @@ namespace headless {
class DeterministicDispatcherTest : public ::testing::Test {
protected:
- DeterministicDispatcherTest() {}
- ~DeterministicDispatcherTest() override {}
+ DeterministicDispatcherTest() = default;
+ ~DeterministicDispatcherTest() override = default;
void SetUp() override {
deterministic_dispatcher_.reset(
@@ -135,7 +135,7 @@ class NavigationRequestForTest : public NavigationRequest {
explicit NavigationRequestForTest(base::Closure* done_closure)
: done_closure_(done_closure) {}
- ~NavigationRequestForTest() override {}
+ ~NavigationRequestForTest() override = default;
// NavigationRequest implementation:
void StartProcessing(base::Closure done_callback) override {
diff --git a/chromium/headless/public/util/deterministic_http_protocol_handler.cc b/chromium/headless/public/util/deterministic_http_protocol_handler.cc
index 8ff8e37df81..84cc84875be 100644
--- a/chromium/headless/public/util/deterministic_http_protocol_handler.cc
+++ b/chromium/headless/public/util/deterministic_http_protocol_handler.cc
@@ -19,8 +19,8 @@ namespace headless {
class DeterministicHttpProtocolHandler::NopGenericURLRequestJobDelegate
: public GenericURLRequestJob::Delegate {
public:
- NopGenericURLRequestJobDelegate() {}
- ~NopGenericURLRequestJobDelegate() override {}
+ NopGenericURLRequestJobDelegate() = default;
+ ~NopGenericURLRequestJobDelegate() override = default;
void OnResourceLoadFailed(const Request* request, net::Error error) override {
}
diff --git a/chromium/headless/public/util/error_reporter.cc b/chromium/headless/public/util/error_reporter.cc
index 9daa0198670..7f4df916177 100644
--- a/chromium/headless/public/util/error_reporter.cc
+++ b/chromium/headless/public/util/error_reporter.cc
@@ -7,12 +7,13 @@
#include <sstream>
#include "base/logging.h"
+#include "base/strings/string_util.h"
namespace headless {
-ErrorReporter::ErrorReporter() {}
+ErrorReporter::ErrorReporter() = default;
-ErrorReporter::~ErrorReporter() {}
+ErrorReporter::~ErrorReporter() = default;
#if DCHECK_IS_ON()
void ErrorReporter::Push() {
@@ -48,6 +49,10 @@ void ErrorReporter::AddError(base::StringPiece description) {
bool ErrorReporter::HasErrors() const {
return !errors_.empty();
}
+
+std::string ErrorReporter::ToString() const {
+ return base::JoinString(errors_, ", ");
+}
#endif // DCHECK_IS_ON()
} // namespace headless
diff --git a/chromium/headless/public/util/error_reporter.h b/chromium/headless/public/util/error_reporter.h
index 2fd432f3f2d..c38c60c8940 100644
--- a/chromium/headless/public/util/error_reporter.h
+++ b/chromium/headless/public/util/error_reporter.h
@@ -39,13 +39,17 @@ class HEADLESS_EXPORT ErrorReporter {
// Returns a list of reported errors.
const std::vector<std::string>& errors() const { return errors_; }
-#else // DCHECK_IS_ON()
- inline void Push() {}
- inline void Pop() {}
- inline void SetName(const char* name) {}
- inline void AddError(base::StringPiece description) {}
- inline bool HasErrors() const { return false; }
- std::vector<std::string> errors() const { return std::vector<std::string>(); }
+
+ // Returns a string containing all the errors concatenated together.
+ std::string ToString() const;
+#else // DCHECK_IS_ON()
+ void Push() {}
+ void Pop() {}
+ void SetName(const char* name) {}
+ void AddError(base::StringPiece description) {}
+ bool HasErrors() const { return false; }
+ std::vector<std::string> errors() const { return {}; }
+ std::string ToString() const { return ""; }
#endif // DCHECK_IS_ON()
private:
diff --git a/chromium/headless/public/util/expedited_dispatcher.cc b/chromium/headless/public/util/expedited_dispatcher.cc
index 649321a7ad0..1fe445cca88 100644
--- a/chromium/headless/public/util/expedited_dispatcher.cc
+++ b/chromium/headless/public/util/expedited_dispatcher.cc
@@ -16,7 +16,7 @@ ExpeditedDispatcher::ExpeditedDispatcher(
scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner)
: io_thread_task_runner_(std::move(io_thread_task_runner)) {}
-ExpeditedDispatcher::~ExpeditedDispatcher() {}
+ExpeditedDispatcher::~ExpeditedDispatcher() = default;
void ExpeditedDispatcher::JobCreated(ManagedDispatchURLRequestJob*) {}
diff --git a/chromium/headless/public/util/expedited_dispatcher_test.cc b/chromium/headless/public/util/expedited_dispatcher_test.cc
index dacc82deab0..05571f6df02 100644
--- a/chromium/headless/public/util/expedited_dispatcher_test.cc
+++ b/chromium/headless/public/util/expedited_dispatcher_test.cc
@@ -23,8 +23,8 @@ namespace headless {
class ExpeditedDispatcherTest : public ::testing::Test {
protected:
- ExpeditedDispatcherTest() {}
- ~ExpeditedDispatcherTest() override {}
+ ExpeditedDispatcherTest() = default;
+ ~ExpeditedDispatcherTest() override = default;
void SetUp() override {
expedited_dispatcher_.reset(new ExpeditedDispatcher(loop_.task_runner()));
@@ -92,7 +92,7 @@ class NavigationRequestForTest : public NavigationRequest {
explicit NavigationRequestForTest(base::Closure* done_closure)
: done_closure_(done_closure) {}
- ~NavigationRequestForTest() override {}
+ ~NavigationRequestForTest() override = default;
// NavigationRequest implementation:
void StartProcessing(base::Closure done_callback) override {
diff --git a/chromium/headless/public/util/generic_url_request_job.cc b/chromium/headless/public/util/generic_url_request_job.cc
index ff2dfe86d38..e865fae2f50 100644
--- a/chromium/headless/public/util/generic_url_request_job.cc
+++ b/chromium/headless/public/util/generic_url_request_job.cc
@@ -8,7 +8,6 @@
#include <algorithm>
#include "base/logging.h"
-#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "content/public/browser/devtools_agent_host.h"
#include "content/public/browser/render_frame_host.h"
@@ -21,6 +20,7 @@
#include "net/base/net_errors.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "net/base/upload_bytes_element_reader.h"
+#include "net/cookies/canonical_cookie.h"
#include "net/cookies/cookie_store.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_util.h"
@@ -78,7 +78,7 @@ void GenericURLRequestJob::SetExtraRequestHeaders(
void GenericURLRequestJob::Start() {
PrepareCookies(request_->url(), request_->method(),
- url::Origin(request_->site_for_cookies()));
+ url::Origin::Create(request_->site_for_cookies()));
}
void GenericURLRequestJob::PrepareCookies(const GURL& rewritten_url,
@@ -90,7 +90,7 @@ void GenericURLRequestJob::PrepareCookies(const GURL& rewritten_url,
options.set_include_httponly();
// See net::URLRequestHttpJob::AddCookieHeaderAndStart().
- url::Origin requested_origin(rewritten_url);
+ url::Origin requested_origin = url::Origin::Create(rewritten_url);
if (net::registry_controlled_domains::SameDomainOrHost(
requested_origin, site_for_cookies,
net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) {
@@ -118,7 +118,7 @@ void GenericURLRequestJob::OnCookiesAvailable(
DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
// TODO(alexclarke): Set user agent.
// Pass cookies, the referrer and any extra headers into the fetch request.
- std::string cookie = net::CookieStore::BuildCookieLine(cookie_list);
+ std::string cookie = net::CanonicalCookie::BuildCookieLine(cookie_list);
if (!cookie.empty())
extra_request_headers_.SetHeader(net::HttpRequestHeaders::kCookie, cookie);
@@ -299,99 +299,133 @@ bool GenericURLRequestJob::IsAsync() const {
return true;
}
+bool GenericURLRequestJob::IsBrowserSideFetch() const {
+ if (!request_resource_info_)
+ return false;
+ return request_resource_info_->GetFrameTreeNodeId() != -1;
+}
+
namespace {
void CompletionCallback(int* dest, base::Closure* quit_closure, int value) {
*dest = value;
quit_closure->Run();
}
+
+bool ReadAndAppendBytes(const std::unique_ptr<net::UploadElementReader>& reader,
+ std::string& post_data) {
+ // TODO(alexclarke): Consider changing the interface of
+ // GenericURLRequestJob::GetPostData to use a callback which would let us
+ // avoid the nested run loops below.
+
+ // Read the POST bytes.
+ uint64_t size_to_stop_at = post_data.size() + reader->GetContentLength();
+ const size_t block_size = 1024;
+ scoped_refptr<net::IOBuffer> read_buffer(new net::IOBuffer(block_size));
+ while (post_data.size() < size_to_stop_at) {
+ base::Closure quit_closure;
+ int bytes_read = reader->Read(
+ read_buffer.get(), block_size,
+ base::Bind(&CompletionCallback, &bytes_read, &quit_closure));
+
+ if (bytes_read == net::ERR_IO_PENDING) {
+ base::RunLoop nested_run_loop(base::RunLoop::Type::kNestableTasksAllowed);
+ quit_closure = nested_run_loop.QuitClosure();
+ nested_run_loop.Run();
+ }
+
+ // Bail out if an error occured.
+ if (bytes_read < 0)
+ return false;
+
+ post_data.append(read_buffer->data(), bytes_read);
+ }
+ return true;
+}
} // namespace
-std::string GenericURLRequestJob::GetPostData() const {
+const std::vector<std::unique_ptr<net::UploadElementReader>>*
+GenericURLRequestJob::GetInitializedReaders() const {
if (!request_->has_upload())
- return "";
+ return nullptr;
const net::UploadDataStream* stream = request_->get_upload();
if (!stream->GetElementReaders())
- return "";
+ return nullptr;
if (stream->GetElementReaders()->size() == 0)
- return "";
+ return nullptr;
- DCHECK_EQ(1u, stream->GetElementReaders()->size());
- const std::unique_ptr<net::UploadElementReader>& reader =
- (*stream->GetElementReaders())[0];
- // If |reader| is actually an UploadBytesElementReader we can get the data
- // directly (should be faster than the horrible stuff below).
- const net::UploadBytesElementReader* bytes_reader = reader->AsBytesReader();
- if (bytes_reader)
- return std::string(bytes_reader->bytes(), bytes_reader->length());
+ const std::vector<std::unique_ptr<net::UploadElementReader>>* readers =
+ stream->GetElementReaders();
- // TODO(alexclarke): Consider changing the interface of
- // GenericURLRequestJob::GetPostData to use a callback which would let us
- // avoid the nested run loops below.
+ // Initialize the readers, this necessary because some of them will segfault
+ // if you try to call any method without initializing first.
+ for (size_t i = 0; i < readers->size(); ++i) {
+ const std::unique_ptr<net::UploadElementReader>& reader = (*readers)[i];
+ if (!reader)
+ continue;
- // Initialize the reader.
- {
base::Closure quit_closure;
int init_result = reader->Init(
base::Bind(&CompletionCallback, &init_result, &quit_closure));
if (init_result == net::ERR_IO_PENDING) {
- base::RunLoop nested_run_loop;
- base::MessageLoop::ScopedNestableTaskAllower allow_nested(
- base::MessageLoop::current());
+ base::RunLoop nested_run_loop(base::RunLoop::Type::kNestableTasksAllowed);
quit_closure = nested_run_loop.QuitClosure();
nested_run_loop.Run();
}
if (init_result != net::OK)
- return "";
+ return nullptr;
}
- // Read the POST bytes.
- uint64_t content_length = reader->GetContentLength();
- std::string post_data;
- post_data.reserve(content_length);
- const size_t block_size = 1024;
- scoped_refptr<net::IOBuffer> read_buffer(new net::IOBuffer(block_size));
- while (post_data.size() < content_length) {
- base::Closure quit_closure;
- int bytes_read = reader->Read(
- read_buffer.get(), block_size,
- base::Bind(&CompletionCallback, &bytes_read, &quit_closure));
+ return readers;
+}
- if (bytes_read == net::ERR_IO_PENDING) {
- base::MessageLoop::ScopedNestableTaskAllower allow_nested(
- base::MessageLoop::current());
- base::RunLoop nested_run_loop;
- quit_closure = nested_run_loop.QuitClosure();
- nested_run_loop.Run();
- }
+std::string GenericURLRequestJob::GetPostData() const {
+ const std::vector<std::unique_ptr<net::UploadElementReader>>* readers =
+ GetInitializedReaders();
- // Bail out if an error occured.
- if (bytes_read < 0)
- return "";
+ if (!readers)
+ return "";
- post_data.append(read_buffer->data(), bytes_read);
+ uint64_t total_content_length = 0;
+ for (size_t i = 0; i < readers->size(); ++i) {
+ const std::unique_ptr<net::UploadElementReader>& reader = (*readers)[i];
+ total_content_length += reader->GetContentLength();
+ }
+ std::string post_data;
+ post_data.reserve(total_content_length);
+
+ for (size_t i = 0; i < readers->size(); ++i) {
+ const std::unique_ptr<net::UploadElementReader>& reader = (*readers)[i];
+ // If |reader| is actually an UploadBytesElementReader we can get the data
+ // directly.
+ const net::UploadBytesElementReader* bytes_reader = reader->AsBytesReader();
+ if (bytes_reader) {
+ post_data.append(bytes_reader->bytes(), bytes_reader->length());
+ } else {
+ if (!ReadAndAppendBytes(reader, post_data))
+ break; // Bail out if something went wrong.
+ }
}
return post_data;
}
uint64_t GenericURLRequestJob::GetPostDataSize() const {
- if (!request_->has_upload())
- return 0;
-
- const net::UploadDataStream* stream = request_->get_upload();
- if (!stream->GetElementReaders())
- return 0;
+ const std::vector<std::unique_ptr<net::UploadElementReader>>* readers =
+ GetInitializedReaders();
- if (stream->GetElementReaders()->size() == 0)
+ if (!readers)
return 0;
- DCHECK_EQ(1u, stream->GetElementReaders()->size());
- const std::unique_ptr<net::UploadElementReader>& reader =
- (*stream->GetElementReaders())[0];
- return reader->GetContentLength();
+ uint64_t total_content_length = 0;
+ for (size_t i = 0; i < readers->size(); ++i) {
+ const std::unique_ptr<net::UploadElementReader>& reader = (*readers)[i];
+ if (reader)
+ total_content_length += reader->GetContentLength();
+ }
+ return total_content_length;
}
} // namespace headless
diff --git a/chromium/headless/public/util/generic_url_request_job.h b/chromium/headless/public/util/generic_url_request_job.h
index 192e7545104..06c90f7296e 100644
--- a/chromium/headless/public/util/generic_url_request_job.h
+++ b/chromium/headless/public/util/generic_url_request_job.h
@@ -24,6 +24,7 @@
namespace net {
class IOBuffer;
+class UploadElementReader;
} // namespace net
namespace content {
@@ -56,6 +57,9 @@ class HEADLESS_EXPORT Request {
// Returns the size of the POST data, if any, from the net::URLRequest.
virtual uint64_t GetPostDataSize() const = 0;
+ // Returns true if the fetch was issues by the browser.
+ virtual bool IsBrowserSideFetch() const = 0;
+
enum class ResourceType {
MAIN_FRAME = 0,
SUB_FRAME = 1,
@@ -159,6 +163,7 @@ class HEADLESS_EXPORT GenericURLRequestJob
uint64_t GetPostDataSize() const override;
ResourceType GetResourceType() const override;
bool IsAsync() const override;
+ bool IsBrowserSideFetch() const override;
private:
void PrepareCookies(const GURL& rewritten_url,
@@ -168,6 +173,9 @@ class HEADLESS_EXPORT GenericURLRequestJob
const std::string& method,
const net::CookieList& cookie_list);
+ const std::vector<std::unique_ptr<net::UploadElementReader>>*
+ GetInitializedReaders() const;
+
std::unique_ptr<URLFetcher> url_fetcher_;
net::HttpRequestHeaders extra_request_headers_;
scoped_refptr<net::HttpResponseHeaders> response_headers_;
diff --git a/chromium/headless/public/util/generic_url_request_job_test.cc b/chromium/headless/public/util/generic_url_request_job_test.cc
index e278720295b..777837d4b03 100644
--- a/chromium/headless/public/util/generic_url_request_job_test.cc
+++ b/chromium/headless/public/util/generic_url_request_job_test.cc
@@ -28,6 +28,7 @@
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+using testing::NotNull;
using testing::_;
std::ostream& operator<<(std::ostream& os, const base::DictionaryValue& value) {
@@ -57,13 +58,15 @@ class MockDelegate : public MockGenericURLRequestJobDelegate {
class MockFetcher : public URLFetcher {
public:
MockFetcher(base::DictionaryValue* fetch_request,
+ std::string* received_post_data,
std::map<std::string, std::string>* json_fetch_reply_map,
base::Callback<void(const Request*)>* on_request_callback)
: json_fetch_reply_map_(json_fetch_reply_map),
fetch_request_(fetch_request),
+ received_post_data_(received_post_data),
on_request_callback_(on_request_callback) {}
- ~MockFetcher() override {}
+ ~MockFetcher() override = default;
void StartFetch(const Request* request,
ResultListener* result_listener) override {
@@ -80,9 +83,9 @@ class MockFetcher : public URLFetcher {
headers->SetString(it.name(), it.value());
}
fetch_request_->Set("headers", std::move(headers));
- std::string post_data = request->GetPostData();
- if (!post_data.empty())
- fetch_request_->SetString("post_data", std::move(post_data));
+ *received_post_data_ = request->GetPostData();
+ if (!received_post_data_->empty() && received_post_data_->size() < 1024)
+ fetch_request_->SetString("post_data", *received_post_data_);
const auto find_it = json_fetch_reply_map_->find(url);
if (find_it == json_fetch_reply_map_->end()) {
@@ -97,9 +100,6 @@ class MockFetcher : public URLFetcher {
base::DictionaryValue* reply_dictionary;
ASSERT_TRUE(fetch_reply->GetAsDictionary(&reply_dictionary));
- std::string final_url;
- ASSERT_TRUE(reply_dictionary->GetString("url", &final_url));
- ASSERT_TRUE(reply_dictionary->GetString("data", &response_data_));
base::DictionaryValue* reply_headers_dictionary;
ASSERT_TRUE(
reply_dictionary->GetDictionary("headers", &reply_headers_dictionary));
@@ -107,10 +107,8 @@ class MockFetcher : public URLFetcher {
new net::HttpResponseHeaders(""));
for (base::DictionaryValue::Iterator it(*reply_headers_dictionary);
!it.IsAtEnd(); it.Advance()) {
- std::string value;
- ASSERT_TRUE(it.value().GetAsString(&value));
- response_headers->AddHeader(
- base::StringPrintf("%s: %s", it.key().c_str(), value.c_str()));
+ response_headers->AddHeader(base::StringPrintf(
+ "%s: %s", it.key().c_str(), it.value().GetString().c_str()));
}
// Set the fields needed for tracing, so that we can check
@@ -118,14 +116,20 @@ class MockFetcher : public URLFetcher {
net::LoadTimingInfo load_timing_info;
load_timing_info.send_start = base::TimeTicks::Max();
load_timing_info.receive_headers_end = base::TimeTicks::Max();
+ const base::Value* final_url_value = reply_dictionary->FindKey("url");
+ ASSERT_THAT(final_url_value, NotNull());
+ const base::Value* response_data_value = reply_dictionary->FindKey("data");
+ ASSERT_THAT(response_data_value, NotNull());
+ response_data_ = response_data_value->GetString();
result_listener->OnFetchComplete(
- GURL(final_url), std::move(response_headers), response_data_.c_str(),
- response_data_.size(), load_timing_info);
+ GURL(final_url_value->GetString()), std::move(response_headers),
+ response_data_.c_str(), response_data_.size(), load_timing_info);
}
private:
- std::map<std::string, std::string>* json_fetch_reply_map_; // NOT OWNED
- base::DictionaryValue* fetch_request_; // NOT OWNED
+ std::map<std::string, std::string>* json_fetch_reply_map_; // NOT OWNED
+ base::DictionaryValue* fetch_request_; // NOT OWNED
+ std::string* received_post_data_; // NOT OWNED
base::Callback<void(const Request*)>* on_request_callback_; // NOT OWNED
std::string response_data_; // Here to ensure the required lifetime.
};
@@ -135,11 +139,13 @@ class MockProtocolHandler : public net::URLRequestJobFactory::ProtocolHandler {
// Details of the fetch will be stored in |fetch_request|.
// The fetch response will be created from parsing |json_fetch_reply_map|.
MockProtocolHandler(base::DictionaryValue* fetch_request,
+ std::string* received_post_data,
std::map<std::string, std::string>* json_fetch_reply_map,
URLRequestDispatcher* dispatcher,
GenericURLRequestJob::Delegate* job_delegate,
base::Callback<void(const Request*)>* on_request_callback)
: fetch_request_(fetch_request),
+ received_post_data_(received_post_data),
json_fetch_reply_map_(json_fetch_reply_map),
job_delegate_(job_delegate),
dispatcher_(dispatcher),
@@ -151,13 +157,15 @@ class MockProtocolHandler : public net::URLRequestJobFactory::ProtocolHandler {
net::NetworkDelegate* network_delegate) const override {
return new GenericURLRequestJob(
request, network_delegate, dispatcher_,
- base::MakeUnique<MockFetcher>(fetch_request_, json_fetch_reply_map_,
+ base::MakeUnique<MockFetcher>(fetch_request_, received_post_data_,
+ json_fetch_reply_map_,
on_request_callback_),
job_delegate_, nullptr);
}
private:
base::DictionaryValue* fetch_request_; // NOT OWNED
+ std::string* received_post_data_; // NOT OWNED
std::map<std::string, std::string>* json_fetch_reply_map_; // NOT OWNED
GenericURLRequestJob::Delegate* job_delegate_; // NOT OWNED
URLRequestDispatcher* dispatcher_; // NOT OWNED
@@ -170,9 +178,10 @@ class GenericURLRequestJobTest : public testing::Test {
public:
GenericURLRequestJobTest() : dispatcher_(message_loop_.task_runner()) {
url_request_job_factory_.SetProtocolHandler(
- "https", base::WrapUnique(new MockProtocolHandler(
- &fetch_request_, &json_fetch_reply_map_, &dispatcher_,
- &job_delegate_, &on_request_callback_)));
+ "https",
+ base::WrapUnique(new MockProtocolHandler(
+ &fetch_request_, &received_post_data_, &json_fetch_reply_map_,
+ &dispatcher_, &job_delegate_, &on_request_callback_)));
url_request_context_.set_job_factory(&url_request_job_factory_);
url_request_context_.set_cookie_store(&cookie_store_);
}
@@ -219,6 +228,7 @@ class GenericURLRequestJobTest : public testing::Test {
MockURLRequestDelegate request_delegate_;
base::DictionaryValue fetch_request_; // The request sent to MockFetcher.
+ std::string received_post_data_; // The POST data (useful if large).
std::map<std::string, std::string>
json_fetch_reply_map_; // Replies to be sent by MockFetcher.
MockDelegate job_delegate_;
@@ -304,6 +314,44 @@ TEST_F(GenericURLRequestJobTest, BasicPostRequestParams) {
EXPECT_THAT(fetch_request_, MatchesJson(expected_request_json));
}
+TEST_F(GenericURLRequestJobTest, LargePostData) {
+ json_fetch_reply_map_["https://example.com/"] = R"(
+ {
+ "url": "https://example.com",
+ "data": "Reply",
+ "headers": {
+ "Content-Type": "text/html; charset=UTF-8"
+ }
+ })";
+
+ std::unique_ptr<net::URLRequest> request(url_request_context_.CreateRequest(
+ GURL("https://example.com"), net::DEFAULT_PRIORITY, &request_delegate_,
+ TRAFFIC_ANNOTATION_FOR_TESTS));
+ request->SetReferrer("https://referrer.example.com");
+ request->SetExtraRequestHeaderByName("Extra-Header", "Value", true);
+ request->SetExtraRequestHeaderByName("User-Agent", "TestBrowser", true);
+ request->SetExtraRequestHeaderByName("Accept", "text/plain", true);
+ request->set_method("POST");
+
+ std::vector<char> post_data(4000000);
+ for (size_t i = 0; i < post_data.size(); i++)
+ post_data[i] = i & 127;
+
+ request->set_upload(net::ElementsUploadDataStream::CreateWithReader(
+ base::MakeUnique<net::UploadBytesElementReader>(&post_data[0],
+ post_data.size()),
+ 0));
+ request->Start();
+ base::RunLoop().RunUntilIdle();
+
+ // Make sure we captured the expected post.
+ for (size_t i = 0; i < received_post_data_.size(); i++) {
+ EXPECT_EQ(static_cast<char>(i & 127), post_data[i]);
+ }
+
+ EXPECT_EQ(post_data.size(), received_post_data_.size());
+}
+
TEST_F(GenericURLRequestJobTest, BasicRequestProperties) {
std::string reply = R"(
{
@@ -622,4 +670,76 @@ TEST_F(GenericURLRequestJobTest, GetPostDataAsync) {
EXPECT_EQ(post_data_size, post_data.size());
}
+TEST_F(GenericURLRequestJobTest, LargePostDataNotByteReader) {
+ json_fetch_reply_map_["https://example.com/"] = R"(
+ {
+ "url": "https://example.com",
+ "data": "Reply",
+ "headers": {
+ "Content-Type": "text/html; charset=UTF-8"
+ }
+ })";
+
+ std::unique_ptr<net::URLRequest> request(url_request_context_.CreateRequest(
+ GURL("https://example.com"), net::DEFAULT_PRIORITY, &request_delegate_,
+ TRAFFIC_ANNOTATION_FOR_TESTS));
+ request->SetReferrer("https://referrer.example.com");
+ request->SetExtraRequestHeaderByName("Extra-Header", "Value", true);
+ request->SetExtraRequestHeaderByName("User-Agent", "TestBrowser", true);
+ request->SetExtraRequestHeaderByName("Accept", "text/plain", true);
+ request->set_method("POST");
+
+ std::string post_data;
+ post_data.reserve(4000000);
+ for (size_t i = 0; i < post_data.size(); i++)
+ post_data.at(i) = i & 127;
+
+ request->set_upload(net::ElementsUploadDataStream::CreateWithReader(
+ base::MakeUnique<ByteAtATimeUploadElementReader>(post_data), 0));
+ request->Start();
+ base::RunLoop().RunUntilIdle();
+
+ // Make sure we captured the expected post.
+ for (size_t i = 0; i < received_post_data_.size(); i++) {
+ EXPECT_EQ(static_cast<char>(i & 127), post_data[i]);
+ }
+
+ EXPECT_EQ(post_data.size(), received_post_data_.size());
+}
+
+TEST_F(GenericURLRequestJobTest, PostWithMultipleElements) {
+ json_fetch_reply_map_["https://example.com/"] = R"(
+ {
+ "url": "https://example.com",
+ "data": "Reply",
+ "headers": {
+ "Content-Type": "text/html; charset=UTF-8"
+ }
+ })";
+
+ std::unique_ptr<net::URLRequest> request(url_request_context_.CreateRequest(
+ GURL("https://example.com"), net::DEFAULT_PRIORITY, &request_delegate_,
+ TRAFFIC_ANNOTATION_FOR_TESTS));
+ request->SetReferrer("https://referrer.example.com");
+ request->SetExtraRequestHeaderByName("Extra-Header", "Value", true);
+ request->SetExtraRequestHeaderByName("User-Agent", "TestBrowser", true);
+ request->SetExtraRequestHeaderByName("Accept", "text/plain", true);
+ request->set_method("POST");
+
+ std::vector<std::unique_ptr<net::UploadElementReader>> element_readers;
+ element_readers.push_back(
+ base::MakeUnique<ByteAtATimeUploadElementReader>("Does "));
+ element_readers.push_back(
+ base::MakeUnique<ByteAtATimeUploadElementReader>("this "));
+ element_readers.push_back(
+ base::MakeUnique<ByteAtATimeUploadElementReader>("work?"));
+
+ request->set_upload(base::MakeUnique<net::ElementsUploadDataStream>(
+ std::move(element_readers), 0));
+ request->Start();
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ("Does this work?", received_post_data_);
+}
+
} // namespace headless
diff --git a/chromium/headless/public/util/http_url_fetcher.cc b/chromium/headless/public/util/http_url_fetcher.cc
index 20e44ea93de..0e83ebb970f 100644
--- a/chromium/headless/public/util/http_url_fetcher.cc
+++ b/chromium/headless/public/util/http_url_fetcher.cc
@@ -116,7 +116,7 @@ HttpURLFetcher::Delegate::Delegate(
request_->Start();
}
-HttpURLFetcher::Delegate::~Delegate() {}
+HttpURLFetcher::Delegate::~Delegate() = default;
void HttpURLFetcher::Delegate::OnAuthRequired(
net::URLRequest* request,
@@ -228,7 +228,7 @@ HttpURLFetcher::HttpURLFetcher(
const net::URLRequestContext* url_request_context)
: url_request_context_(url_request_context) {}
-HttpURLFetcher::~HttpURLFetcher() {}
+HttpURLFetcher::~HttpURLFetcher() = default;
void HttpURLFetcher::StartFetch(const Request* request,
ResultListener* result_listener) {
diff --git a/chromium/headless/public/util/in_memory_protocol_handler.cc b/chromium/headless/public/util/in_memory_protocol_handler.cc
index 27c076551db..49dd8c01657 100644
--- a/chromium/headless/public/util/in_memory_protocol_handler.cc
+++ b/chromium/headless/public/util/in_memory_protocol_handler.cc
@@ -8,8 +8,8 @@
namespace headless {
-InMemoryProtocolHandler::InMemoryProtocolHandler() {}
-InMemoryProtocolHandler::~InMemoryProtocolHandler() {}
+InMemoryProtocolHandler::InMemoryProtocolHandler() = default;
+InMemoryProtocolHandler::~InMemoryProtocolHandler() = default;
net::URLRequestJob* InMemoryProtocolHandler::MaybeCreateJob(
net::URLRequest* request,
diff --git a/chromium/headless/public/util/in_memory_request_job.cc b/chromium/headless/public/util/in_memory_request_job.cc
index 5fa84151980..4e9451869c4 100644
--- a/chromium/headless/public/util/in_memory_request_job.cc
+++ b/chromium/headless/public/util/in_memory_request_job.cc
@@ -21,7 +21,7 @@ InMemoryRequestJob::InMemoryRequestJob(
data_offset_(0),
weak_factory_(this) {}
-InMemoryRequestJob::~InMemoryRequestJob() {}
+InMemoryRequestJob::~InMemoryRequestJob() = default;
void InMemoryRequestJob::Start() {
// Start reading asynchronously so that all error reporting and data
diff --git a/chromium/headless/public/util/moveable_auto_lock.h b/chromium/headless/public/util/moveable_auto_lock.h
new file mode 100644
index 00000000000..a13cf78b110
--- /dev/null
+++ b/chromium/headless/public/util/moveable_auto_lock.h
@@ -0,0 +1,64 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef HEADLESS_PUBLIC_UTIL_MOVEABLE_AUTO_LOCK_H_
+#define HEADLESS_PUBLIC_UTIL_MOVEABLE_AUTO_LOCK_H_
+
+#include "base/synchronization/lock.h"
+
+namespace headless {
+
+class MoveableAutoLock {
+ public:
+ explicit MoveableAutoLock(base::Lock& lock) : lock_(lock), moved_(false) {
+ lock_.Acquire();
+ }
+
+ MoveableAutoLock(MoveableAutoLock&& other)
+ : lock_(other.lock_), moved_(other.moved_) {
+ lock_.AssertAcquired();
+ other.moved_ = true;
+ }
+
+ ~MoveableAutoLock() {
+ if (moved_)
+ return;
+ lock_.AssertAcquired();
+ lock_.Release();
+ }
+
+ private:
+ base::Lock& lock_;
+ bool moved_;
+ DISALLOW_COPY_AND_ASSIGN(MoveableAutoLock);
+};
+
+// RAII helper to allow threadsafe access to an object guarded by a lock.
+template <class T>
+class LockedPtr {
+ public:
+ LockedPtr(MoveableAutoLock&& lock, T* object)
+ : lock_(std::move(lock)), object_(object) {}
+
+ LockedPtr(LockedPtr&& other)
+ : lock_(std::move(other.lock_)), object_(other.object_) {}
+
+ T* operator->() { return object_; }
+
+ T& operator*() { return *object_; }
+
+ T* Get() { return object_; }
+
+ explicit operator bool() const { return object_; }
+
+ private:
+ MoveableAutoLock lock_;
+ T* object_;
+
+ DISALLOW_COPY_AND_ASSIGN(LockedPtr);
+};
+
+} // namespace headless
+
+#endif // HEADLESS_PUBLIC_UTIL_MOVEABLE_AUTO_LOCK_H_
diff --git a/chromium/headless/public/util/testing/generic_url_request_mocks.cc b/chromium/headless/public/util/testing/generic_url_request_mocks.cc
index c811651ec15..9c20668d5d9 100644
--- a/chromium/headless/public/util/testing/generic_url_request_mocks.cc
+++ b/chromium/headless/public/util/testing/generic_url_request_mocks.cc
@@ -20,7 +20,7 @@ namespace headless {
MockGenericURLRequestJobDelegate::MockGenericURLRequestJobDelegate()
: main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()) {}
-MockGenericURLRequestJobDelegate::~MockGenericURLRequestJobDelegate() {}
+MockGenericURLRequestJobDelegate::~MockGenericURLRequestJobDelegate() = default;
void MockGenericURLRequestJobDelegate::OnResourceLoadFailed(
const Request* request,
@@ -34,8 +34,8 @@ void MockGenericURLRequestJobDelegate::OnResourceLoadComplete(
size_t body_size) {}
// MockCookieStore
-MockCookieStore::MockCookieStore() {}
-MockCookieStore::~MockCookieStore() {}
+MockCookieStore::MockCookieStore() = default;
+MockCookieStore::~MockCookieStore() = default;
void MockCookieStore::SetCookieWithOptionsAsync(
const GURL& url,
@@ -45,22 +45,6 @@ void MockCookieStore::SetCookieWithOptionsAsync(
CHECK(false);
}
-void MockCookieStore::SetCookieWithDetailsAsync(const GURL& url,
- const std::string& name,
- const std::string& value,
- const std::string& domain,
- const std::string& path,
- base::Time creation_time,
- base::Time expiration_time,
- base::Time last_access_time,
- bool secure,
- bool http_only,
- net::CookieSameSite same_site,
- net::CookiePriority priority,
- SetCookiesCallback callback) {
- CHECK(false);
-}
-
void MockCookieStore::SetCanonicalCookieAsync(
std::unique_ptr<net::CanonicalCookie> cookie,
bool secure_source,
@@ -161,10 +145,11 @@ void MockCookieStore::SendCookies(const GURL& url,
}
// MockURLRequestDelegate
-MockURLRequestDelegate::MockURLRequestDelegate() {}
-MockURLRequestDelegate::~MockURLRequestDelegate() {}
+MockURLRequestDelegate::MockURLRequestDelegate() = default;
+MockURLRequestDelegate::~MockURLRequestDelegate() = default;
-void MockURLRequestDelegate::OnResponseStarted(net::URLRequest* request) {}
+void MockURLRequestDelegate::OnResponseStarted(net::URLRequest* request,
+ int net_error) {}
void MockURLRequestDelegate::OnReadCompleted(net::URLRequest* request,
int bytes_read) {}
const std::string& MockURLRequestDelegate::response_data() const {
diff --git a/chromium/headless/public/util/testing/generic_url_request_mocks.h b/chromium/headless/public/util/testing/generic_url_request_mocks.h
index 36b049114ea..e1ff2ab014f 100644
--- a/chromium/headless/public/util/testing/generic_url_request_mocks.h
+++ b/chromium/headless/public/util/testing/generic_url_request_mocks.h
@@ -50,20 +50,6 @@ class MockCookieStore : public net::CookieStore {
const net::CookieOptions& options,
SetCookiesCallback callback) override;
- void SetCookieWithDetailsAsync(const GURL& url,
- const std::string& name,
- const std::string& value,
- const std::string& domain,
- const std::string& path,
- base::Time creation_time,
- base::Time expiration_time,
- base::Time last_access_time,
- bool secure,
- bool http_only,
- net::CookieSameSite same_site,
- net::CookiePriority priority,
- SetCookiesCallback callback) override;
-
void SetCanonicalCookieAsync(std::unique_ptr<net::CanonicalCookie> cookie,
bool secure_source,
bool modify_http_only,
@@ -129,7 +115,7 @@ class MockURLRequestDelegate : public net::URLRequest::Delegate {
MockURLRequestDelegate();
~MockURLRequestDelegate() override;
- void OnResponseStarted(net::URLRequest* request) override;
+ void OnResponseStarted(net::URLRequest* request, int net_error) override;
void OnReadCompleted(net::URLRequest* request, int bytes_read) override;
const std::string& response_data() const;
const net::IOBufferWithSize* metadata() const;
diff --git a/chromium/headless/public/util/testing/mock_devtools_agent_host.cc b/chromium/headless/public/util/testing/mock_devtools_agent_host.cc
index 78959226a1d..683b4f99d33 100644
--- a/chromium/headless/public/util/testing/mock_devtools_agent_host.cc
+++ b/chromium/headless/public/util/testing/mock_devtools_agent_host.cc
@@ -6,8 +6,8 @@
namespace headless {
-MockDevToolsAgentHost::MockDevToolsAgentHost() {}
+MockDevToolsAgentHost::MockDevToolsAgentHost() = default;
-MockDevToolsAgentHost::~MockDevToolsAgentHost() {}
+MockDevToolsAgentHost::~MockDevToolsAgentHost() = default;
} // namespace headless
diff --git a/chromium/headless/public/util/testing/test_in_memory_protocol_handler.cc b/chromium/headless/public/util/testing/test_in_memory_protocol_handler.cc
index 8864b0b7b44..8883fb1d107 100644
--- a/chromium/headless/public/util/testing/test_in_memory_protocol_handler.cc
+++ b/chromium/headless/public/util/testing/test_in_memory_protocol_handler.cc
@@ -13,12 +13,9 @@ namespace headless {
class TestInMemoryProtocolHandler::MockURLFetcher : public URLFetcher {
public:
- MockURLFetcher(
- TestInMemoryProtocolHandler* protocol_handler,
- scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner)
- : protocol_handler_(protocol_handler),
- io_thread_task_runner_(io_thread_task_runner) {}
- ~MockURLFetcher() override {}
+ explicit MockURLFetcher(TestInMemoryProtocolHandler* protocol_handler)
+ : protocol_handler_(protocol_handler) {}
+ ~MockURLFetcher() override = default;
// URLFetcher implementation:
void StartFetch(const Request* request,
@@ -30,12 +27,10 @@ class TestInMemoryProtocolHandler::MockURLFetcher : public URLFetcher {
DCHECK_NE(devtools_frame_id, "") << " For url " << url;
protocol_handler_->RegisterUrl(url.spec(), devtools_frame_id);
- if (protocol_handler_->simulate_slow_fetch()) {
- io_thread_task_runner_->PostDelayedTask(
- FROM_HERE,
- base::Bind(&MockURLFetcher::FinishFetch, base::Unretained(this),
- result_listener, url),
- base::TimeDelta::FromMilliseconds(100));
+ if (protocol_handler_->request_deferrer()) {
+ protocol_handler_->request_deferrer()->OnRequest(
+ url, base::Bind(&MockURLFetcher::FinishFetch, base::Unretained(this),
+ result_listener, url));
} else {
FinishFetch(result_listener, url);
}
@@ -46,6 +41,7 @@ class TestInMemoryProtocolHandler::MockURLFetcher : public URLFetcher {
protocol_handler_->GetResponse(url.spec());
if (response) {
net::LoadTimingInfo load_timing_info;
+ load_timing_info.receive_headers_end = base::TimeTicks::Now();
result_listener->OnFetchCompleteExtractHeaders(
url, response->data.c_str(), response->data.size(), load_timing_info);
} else {
@@ -55,7 +51,6 @@ class TestInMemoryProtocolHandler::MockURLFetcher : public URLFetcher {
private:
TestInMemoryProtocolHandler* protocol_handler_; // NOT OWNED.
- scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner_;
DISALLOW_COPY_AND_ASSIGN(MockURLFetcher);
};
@@ -63,8 +58,8 @@ class TestInMemoryProtocolHandler::MockURLFetcher : public URLFetcher {
class TestInMemoryProtocolHandler::TestDelegate
: public GenericURLRequestJob::Delegate {
public:
- TestDelegate() {}
- ~TestDelegate() override {}
+ TestDelegate() = default;
+ ~TestDelegate() override = default;
// GenericURLRequestJob::Delegate implementation:
void OnResourceLoadFailed(const Request* request, net::Error error) override {
@@ -85,14 +80,14 @@ class TestInMemoryProtocolHandler::TestDelegate
TestInMemoryProtocolHandler::TestInMemoryProtocolHandler(
scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner,
- bool simulate_slow_fetch)
+ RequestDeferrer* request_deferrer)
: test_delegate_(new TestDelegate()),
dispatcher_(new ExpeditedDispatcher(io_thread_task_runner)),
headless_browser_context_(nullptr),
- simulate_slow_fetch_(simulate_slow_fetch),
+ request_deferrer_(request_deferrer),
io_thread_task_runner_(io_thread_task_runner) {}
-TestInMemoryProtocolHandler::~TestInMemoryProtocolHandler() {}
+TestInMemoryProtocolHandler::~TestInMemoryProtocolHandler() = default;
void TestInMemoryProtocolHandler::SetHeadlessBrowserContext(
HeadlessBrowserContext* headless_browser_context) {
@@ -119,8 +114,7 @@ net::URLRequestJob* TestInMemoryProtocolHandler::MaybeCreateJob(
return new GenericURLRequestJob(
request, network_delegate, dispatcher_.get(),
base::MakeUnique<MockURLFetcher>(
- const_cast<TestInMemoryProtocolHandler*>(this),
- io_thread_task_runner_),
+ const_cast<TestInMemoryProtocolHandler*>(this)),
test_delegate_.get(), headless_browser_context_);
}
diff --git a/chromium/headless/public/util/testing/test_in_memory_protocol_handler.h b/chromium/headless/public/util/testing/test_in_memory_protocol_handler.h
index c2d6d1f48f0..b3265725097 100644
--- a/chromium/headless/public/util/testing/test_in_memory_protocol_handler.h
+++ b/chromium/headless/public/util/testing/test_in_memory_protocol_handler.h
@@ -19,9 +19,21 @@ class HeadlessBrowserContext;
class TestInMemoryProtocolHandler
: public net::URLRequestJobFactory::ProtocolHandler {
public:
+ class RequestDeferrer {
+ public:
+ virtual ~RequestDeferrer() {}
+
+ // Notifies that the target page has made a request for the |url|. The
+ // request will not be completed until |complete_request| is run.
+ // NOTE this will be called on the IO thread.
+ virtual void OnRequest(const GURL& url, base::Closure complete_request) = 0;
+ };
+
+ // Note |request_deferrer| is optional. If the test doesn't need to control
+ // when resources are fulfilled then pass in nullptr.
TestInMemoryProtocolHandler(
scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner,
- bool simulate_slow_fetch);
+ RequestDeferrer* request_deferrer);
~TestInMemoryProtocolHandler() override;
@@ -30,6 +42,7 @@ class TestInMemoryProtocolHandler
struct Response {
Response() {}
+ Response(const std::string& data) : data(data) {}
Response(const std::string& body, const std::string& mime_type)
: data("HTTP/1.1 200 OK\r\nContent-Type: " + mime_type + "\r\n\r\n" +
body) {}
@@ -60,7 +73,7 @@ class TestInMemoryProtocolHandler
url_to_devtools_frame_id_[url] = devtools_frame_id;
}
- bool simulate_slow_fetch() const { return simulate_slow_fetch_; }
+ RequestDeferrer* request_deferrer() const { return request_deferrer_; }
class TestDelegate;
class MockURLFetcher;
@@ -73,7 +86,7 @@ class TestInMemoryProtocolHandler
HeadlessBrowserContext* headless_browser_context_;
std::map<std::string, std::string> url_to_devtools_frame_id_;
std::vector<std::string> urls_requested_;
- bool simulate_slow_fetch_;
+ RequestDeferrer* request_deferrer_; // NOT OWNED.
scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner_;
DISALLOW_COPY_AND_ASSIGN(TestInMemoryProtocolHandler);
diff --git a/chromium/headless/public/util/throttled_dispatcher.cc b/chromium/headless/public/util/throttled_dispatcher.cc
index 48efca74255..0e767605568 100644
--- a/chromium/headless/public/util/throttled_dispatcher.cc
+++ b/chromium/headless/public/util/throttled_dispatcher.cc
@@ -17,7 +17,7 @@ ThrottledDispatcher::ThrottledDispatcher(
: ExpeditedDispatcher(std::move(io_thread_task_runner)),
requests_paused_(false) {}
-ThrottledDispatcher::~ThrottledDispatcher() {}
+ThrottledDispatcher::~ThrottledDispatcher() = default;
void ThrottledDispatcher::PauseRequests() {
base::AutoLock lock(lock_);
diff --git a/chromium/headless/public/util/throttled_dispatcher_test.cc b/chromium/headless/public/util/throttled_dispatcher_test.cc
index 5e3ba3f382b..d37c9fb1b80 100644
--- a/chromium/headless/public/util/throttled_dispatcher_test.cc
+++ b/chromium/headless/public/util/throttled_dispatcher_test.cc
@@ -23,8 +23,8 @@ namespace headless {
class ThrottledDispatcherTest : public ::testing::Test {
protected:
- ThrottledDispatcherTest() {}
- ~ThrottledDispatcherTest() override {}
+ ThrottledDispatcherTest() = default;
+ ~ThrottledDispatcherTest() override = default;
void SetUp() override {
throttled_dispatcher_.reset(new ThrottledDispatcher(loop_.task_runner()));
diff --git a/chromium/headless/public/util/virtual_time_controller.cc b/chromium/headless/public/util/virtual_time_controller.cc
index 5505bcb3d8f..3dd107dc628 100644
--- a/chromium/headless/public/util/virtual_time_controller.cc
+++ b/chromium/headless/public/util/virtual_time_controller.cc
@@ -13,8 +13,10 @@ namespace headless {
using base::TimeDelta;
VirtualTimeController::VirtualTimeController(
- HeadlessDevToolsClient* devtools_client)
- : devtools_client_(devtools_client) {
+ HeadlessDevToolsClient* devtools_client,
+ int max_task_starvation_count)
+ : devtools_client_(devtools_client),
+ max_task_starvation_count_(max_task_starvation_count) {
devtools_client_->GetEmulation()->GetExperimental()->AddObserver(this);
}
@@ -24,7 +26,7 @@ VirtualTimeController::~VirtualTimeController() {
void VirtualTimeController::GrantVirtualTimeBudget(
emulation::VirtualTimePolicy policy,
- int budget_ms,
+ base::TimeDelta budget,
const base::Callback<void()>& set_up_complete_callback,
const base::Callback<void()>& budget_expired_callback) {
DCHECK(!set_up_complete_callback_);
@@ -32,13 +34,19 @@ void VirtualTimeController::GrantVirtualTimeBudget(
set_up_complete_callback_ = set_up_complete_callback;
budget_expired_callback_ = budget_expired_callback;
- requested_budget_ = TimeDelta::FromMilliseconds(budget_ms);
- accumulated_time_ = TimeDelta();
+ requested_budget_ = budget;
+ accumulated_budget_portion_ = TimeDelta();
virtual_time_policy_ = policy;
// Notify tasks of new budget request.
for (TaskEntry& entry : tasks_)
- NotifyTaskBudgetRequested(&entry, budget_ms);
+ entry.ready_to_advance = false;
+
+ iterating_over_tasks_ = true;
+ for (TaskEntry& entry : tasks_)
+ NotifyTaskBudgetRequested(&entry, requested_budget_);
+ iterating_over_tasks_ = false;
+ DeleteTasksIfRequested();
// If there tasks, NotifyTasksAndAdvance is called from TaskReadyToAdvance.
if (tasks_.empty())
@@ -56,22 +64,39 @@ void VirtualTimeController::NotifyTasksAndAdvance() {
// Give at most as much virtual time as available until the next callback.
bool ready_to_advance = true;
- TimeDelta next_budget = requested_budget_ - accumulated_time_;
+ iterating_over_tasks_ = true;
+ TimeDelta next_budget = requested_budget_ - accumulated_budget_portion_;
+ // TODO(alexclarke): This is a little ugly, find a nicer way.
+ for (TaskEntry& entry : tasks_) {
+ if (entry.next_execution_time <= total_elapsed_time_offset_)
+ entry.ready_to_advance = false;
+ }
+
for (TaskEntry& entry : tasks_) {
- if (entry.next_execution_time <= total_elapsed_time_)
+ if (entry.next_execution_time <= total_elapsed_time_offset_)
NotifyTaskIntervalElapsed(&entry);
- ready_to_advance &= entry.ready_to_advance;
- next_budget =
- std::min(next_budget, entry.next_execution_time - total_elapsed_time_);
+ if (tasks_to_delete_.find(entry.task) == tasks_to_delete_.end()) {
+ ready_to_advance &= entry.ready_to_advance;
+ next_budget = std::min(
+ next_budget, entry.next_execution_time - total_elapsed_time_offset_);
+ }
}
+ iterating_over_tasks_ = false;
+ DeleteTasksIfRequested();
if (!ready_to_advance)
return;
- if (accumulated_time_ >= requested_budget_) {
+ if (accumulated_budget_portion_ >= requested_budget_) {
+ for (TaskEntry& entry : tasks_)
+ entry.ready_to_advance = false;
+
+ iterating_over_tasks_ = true;
for (const TaskEntry& entry : tasks_)
- entry.task->BudgetExpired(total_elapsed_time_);
+ entry.task->BudgetExpired(total_elapsed_time_offset_);
+ iterating_over_tasks_ = false;
+ DeleteTasksIfRequested();
auto callback = budget_expired_callback_;
budget_expired_callback_.Reset();
@@ -82,21 +107,31 @@ void VirtualTimeController::NotifyTasksAndAdvance() {
SetVirtualTimePolicy(next_budget);
}
+void VirtualTimeController::DeleteTasksIfRequested() {
+ DCHECK(!iterating_over_tasks_);
+
+ // It's not safe to delete tasks while iterating over |tasks_| so do it now.
+ while (!tasks_to_delete_.empty()) {
+ CancelRepeatingTask(*tasks_to_delete_.begin());
+ tasks_to_delete_.erase(tasks_to_delete_.begin());
+ }
+}
+
void VirtualTimeController::NotifyTaskIntervalElapsed(TaskEntry* entry) {
- entry->ready_to_advance = false;
- entry->next_execution_time = total_elapsed_time_ + entry->interval;
+ DCHECK(!entry->ready_to_advance);
+ entry->next_execution_time = total_elapsed_time_offset_ + entry->interval;
entry->task->IntervalElapsed(
- total_elapsed_time_,
+ total_elapsed_time_offset_,
base::Bind(&VirtualTimeController::TaskReadyToAdvance,
base::Unretained(this), base::Unretained(entry)));
}
void VirtualTimeController::NotifyTaskBudgetRequested(TaskEntry* entry,
- int budget_ms) {
- entry->ready_to_advance = false;
+ base::TimeDelta budget) {
+ DCHECK(!entry->ready_to_advance);
entry->task->BudgetRequested(
- total_elapsed_time_, budget_ms,
+ total_elapsed_time_offset_, budget,
base::Bind(&VirtualTimeController::TaskReadyToAdvance,
base::Unretained(this), base::Unretained(entry)));
}
@@ -106,20 +141,26 @@ void VirtualTimeController::TaskReadyToAdvance(TaskEntry* entry) {
NotifyTasksAndAdvance();
}
-void VirtualTimeController::SetVirtualTimePolicy(const TimeDelta& next_budget) {
+void VirtualTimeController::SetVirtualTimePolicy(base::TimeDelta next_budget) {
last_used_budget_ = next_budget;
virtual_time_active_ = true;
devtools_client_->GetEmulation()->GetExperimental()->SetVirtualTimePolicy(
emulation::SetVirtualTimePolicyParams::Builder()
.SetPolicy(virtual_time_policy_)
- .SetBudget(next_budget.InMilliseconds())
+ .SetBudget(next_budget.InMillisecondsF())
+ .SetMaxVirtualTimeTaskStarvationCount(max_task_starvation_count_)
.Build(),
base::Bind(&VirtualTimeController::SetVirtualTimePolicyDone,
base::Unretained(this)));
}
void VirtualTimeController::SetVirtualTimePolicyDone(
- std::unique_ptr<emulation::SetVirtualTimePolicyResult>) {
+ std::unique_ptr<emulation::SetVirtualTimePolicyResult> result) {
+ if (result) {
+ virtual_time_base_ = base::Time::FromJsTime(result->GetVirtualTimeBase());
+ } else {
+ LOG(WARNING) << "SetVirtualTimePolicy did not succeed";
+ }
if (set_up_complete_callback_) {
set_up_complete_callback_.Run();
set_up_complete_callback_.Reset();
@@ -133,14 +174,14 @@ void VirtualTimeController::OnVirtualTimeBudgetExpired(
if (!budget_expired_callback_)
return;
- accumulated_time_ += last_used_budget_;
- total_elapsed_time_ += last_used_budget_;
+ accumulated_budget_portion_ += last_used_budget_;
+ total_elapsed_time_offset_ += last_used_budget_;
virtual_time_active_ = false;
NotifyTasksAndAdvance();
}
void VirtualTimeController::ScheduleRepeatingTask(RepeatingTask* task,
- int interval_ms) {
+ base::TimeDelta interval) {
if (virtual_time_active_) {
// We cannot accurately modify any previously granted virtual time budget.
LOG(WARNING) << "VirtualTimeController tasks should be added while "
@@ -149,14 +190,26 @@ void VirtualTimeController::ScheduleRepeatingTask(RepeatingTask* task,
TaskEntry entry;
entry.task = task;
- entry.interval = TimeDelta::FromMilliseconds(interval_ms);
- entry.next_execution_time = total_elapsed_time_ + entry.interval;
+ entry.interval = interval;
+ entry.next_execution_time = total_elapsed_time_offset_ + entry.interval;
tasks_.push_back(entry);
}
void VirtualTimeController::CancelRepeatingTask(RepeatingTask* task) {
+ if (iterating_over_tasks_) {
+ tasks_to_delete_.insert(task);
+ return;
+ }
tasks_.remove_if(
[task](const TaskEntry& entry) { return entry.task == task; });
}
+base::Time VirtualTimeController::GetVirtualTimeBase() const {
+ return virtual_time_base_;
+}
+
+base::TimeDelta VirtualTimeController::GetCurrentVirtualTimeOffset() const {
+ return total_elapsed_time_offset_;
+}
+
} // namespace headless
diff --git a/chromium/headless/public/util/virtual_time_controller.h b/chromium/headless/public/util/virtual_time_controller.h
index ffeee16f36b..05503aa11eb 100644
--- a/chromium/headless/public/util/virtual_time_controller.h
+++ b/chromium/headless/public/util/virtual_time_controller.h
@@ -19,11 +19,11 @@ namespace headless {
class HEADLESS_EXPORT VirtualTimeController
: public emulation::ExperimentalObserver {
public:
- explicit VirtualTimeController(HeadlessDevToolsClient* devtools_client);
+ VirtualTimeController(HeadlessDevToolsClient* devtools_client,
+ int max_task_starvation_count = 0);
~VirtualTimeController() override;
- // Grants |budget_ms| milliseconds of virtual time by applying the provided
- // |policy|.
+ // Grants a |budget| of virtual time by applying the provided |policy|.
//
// |set_up_complete_callback|, if set, is run after the (initial) policy was
// applied via DevTools. |budget_expired_callback| will be called when the
@@ -31,9 +31,9 @@ class HEADLESS_EXPORT VirtualTimeController
//
// Should not be called again until the previous invocation's
// budget_expired_callback was executed.
- void GrantVirtualTimeBudget(
+ virtual void GrantVirtualTimeBudget(
emulation::VirtualTimePolicy policy,
- int budget_ms,
+ base::TimeDelta budget,
const base::Callback<void()>& set_up_complete_callback,
const base::Callback<void()>& budget_expired_callback);
@@ -42,35 +42,48 @@ class HEADLESS_EXPORT VirtualTimeController
virtual ~RepeatingTask() {}
// Called when the tasks's requested virtual time interval has elapsed.
- // |virtual_time| is the virtual time duration that has advanced since the
- // page started loading (millisecond granularity). The task should call
- // |continue_callback| when it is ready for virtual time to continue
+ // |virtual_time_offset| is the virtual time duration that has advanced
+ // since the page started loading (millisecond granularity). The task should
+ // call |continue_callback| when it is ready for virtual time to continue
// advancing.
virtual void IntervalElapsed(
- const base::TimeDelta& virtual_time,
+ base::TimeDelta virtual_time_offset,
const base::Callback<void()>& continue_callback) = 0;
// Called when a new virtual time budget grant was requested. The task
// should call |continue_callback| when it is ready for virtual time to
// continue advancing.
virtual void BudgetRequested(
- const base::TimeDelta& virtual_time,
- int requested_budget_ms,
+ base::TimeDelta virtual_time_offset,
+ base::TimeDelta requested_budget,
const base::Callback<void()>& continue_callback) = 0;
// Called when the latest virtual time budget has been used up.
- virtual void BudgetExpired(const base::TimeDelta& virtual_time) = 0;
+ virtual void BudgetExpired(base::TimeDelta virtual_time_offset) = 0;
};
- // Interleaves execution of the provided |task| with advancing of virtual
- // time. The task will be notified whenever another |interval_ms| milliseconds
- // of virtual time have elapsed, as well as when the last granted budget has
- // been used up.
+ // Interleaves execution of the provided |task| with progression of virtual
+ // time. The task will be notified whenever another |interval| of virtual time
+ // have elapsed, as well as when the last granted budget has been used up.
//
// To ensure that the task is notified of elapsed intervals accurately, it
// should be added while virtual time is paused.
- void ScheduleRepeatingTask(RepeatingTask* task, int interval_ms);
- void CancelRepeatingTask(RepeatingTask* task);
+ virtual void ScheduleRepeatingTask(RepeatingTask* task,
+ base::TimeDelta interval);
+ virtual void CancelRepeatingTask(RepeatingTask* task);
+
+ // Returns the time that virtual time offsets are relative to.
+ virtual base::Time GetVirtualTimeBase() const;
+
+ // Returns the current virtual time offset. Only accurate while virtual time
+ // is paused.
+ virtual base::TimeDelta GetCurrentVirtualTimeOffset() const;
+
+ // Returns the current virtual time stamp. Only accurate while virtual time
+ // is paused.
+ base::Time GetCurrentVirtualTime() const {
+ return GetVirtualTimeBase() + GetCurrentVirtualTimeOffset();
+ }
private:
struct TaskEntry {
@@ -86,14 +99,17 @@ class HEADLESS_EXPORT VirtualTimeController
void NotifyTasksAndAdvance();
void NotifyTaskIntervalElapsed(TaskEntry* entry);
- void NotifyTaskBudgetRequested(TaskEntry* entry, int budget_ms);
+ void NotifyTaskBudgetRequested(TaskEntry* entry, base::TimeDelta budget);
void TaskReadyToAdvance(TaskEntry* entry);
- void SetVirtualTimePolicy(const base::TimeDelta& next_budget);
+ void DeleteTasksIfRequested();
+
+ void SetVirtualTimePolicy(base::TimeDelta next_budget);
void SetVirtualTimePolicyDone(
std::unique_ptr<emulation::SetVirtualTimePolicyResult>);
- HeadlessDevToolsClient* devtools_client_; // NOT OWNED
+ HeadlessDevToolsClient* const devtools_client_; // NOT OWNED
+ const int max_task_starvation_count_;
emulation::VirtualTimePolicy virtual_time_policy_ =
emulation::VirtualTimePolicy::ADVANCE;
@@ -101,13 +117,17 @@ class HEADLESS_EXPORT VirtualTimeController
base::Callback<void()> budget_expired_callback_;
bool virtual_time_active_ = false;
- base::TimeDelta total_elapsed_time_;
+ base::TimeDelta total_elapsed_time_offset_;
base::TimeDelta requested_budget_;
base::TimeDelta last_used_budget_;
- base::TimeDelta accumulated_time_;
+ base::TimeDelta accumulated_budget_portion_;
+ // Initial virtual time that virtual time offsets are relative to.
+ base::Time virtual_time_base_;
std::list<TaskEntry> tasks_;
+ std::set<RepeatingTask*> tasks_to_delete_;
bool in_notify_tasks_and_advance_ = false;
+ bool iterating_over_tasks_ = false;
};
} // namespace headless
diff --git a/chromium/headless/public/util/virtual_time_controller_test.cc b/chromium/headless/public/util/virtual_time_controller_test.cc
index 469d6104bb5..483a99bc21a 100644
--- a/chromium/headless/public/util/virtual_time_controller_test.cc
+++ b/chromium/headless/public/util/virtual_time_controller_test.cc
@@ -16,6 +16,8 @@
namespace headless {
+using testing::ElementsAre;
+using testing::Mock;
using testing::Return;
using testing::_;
@@ -29,17 +31,18 @@ class VirtualTimeControllerTest : public ::testing::Test {
EXPECT_CALL(*mock_host_, IsAttached()).WillOnce(Return(false));
EXPECT_CALL(*mock_host_, AttachClient(&client_));
client_.AttachToHost(mock_host_.get());
- controller_ = base::MakeUnique<VirtualTimeController>(&client_);
+ controller_ = base::MakeUnique<VirtualTimeController>(&client_, 0);
}
- ~VirtualTimeControllerTest() override {}
+ ~VirtualTimeControllerTest() override = default;
void GrantVirtualTimeBudget(int budget_ms) {
ASSERT_FALSE(set_up_complete_);
ASSERT_FALSE(budget_expired_);
controller_->GrantVirtualTimeBudget(
- emulation::VirtualTimePolicy::ADVANCE, budget_ms,
+ emulation::VirtualTimePolicy::ADVANCE,
+ base::TimeDelta::FromMilliseconds(budget_ms),
base::Bind(
[](bool* set_up_complete) {
EXPECT_FALSE(*set_up_complete);
@@ -75,16 +78,34 @@ class VirtualTimeControllerTest : public ::testing::Test {
};
TEST_F(VirtualTimeControllerTest, AdvancesTimeWithoutTasks) {
+ controller_ = base::MakeUnique<VirtualTimeController>(&client_, 1000);
+
+ EXPECT_CALL(*mock_host_,
+ DispatchProtocolMessage(
+ &client_,
+ "{\"id\":0,\"method\":\"Emulation.setVirtualTimePolicy\","
+ "\"params\":{\"budget\":5000.0,"
+ "\"maxVirtualTimeTaskStarvationCount\":1000,"
+ "\"policy\":\"advance\"}}"))
+ .WillOnce(Return(true));
+
+ GrantVirtualTimeBudget(5000);
+}
+
+TEST_F(VirtualTimeControllerTest, MaxVirtualTimeTaskStarvationCount) {
EXPECT_CALL(*mock_host_,
DispatchProtocolMessage(
&client_,
"{\"id\":0,\"method\":\"Emulation.setVirtualTimePolicy\","
- "\"params\":{\"budget\":5000,\"policy\":\"advance\"}}"))
+ "\"params\":{\"budget\":5000.0,"
+ "\"maxVirtualTimeTaskStarvationCount\":0,"
+ "\"policy\":\"advance\"}}"))
.WillOnce(Return(true));
GrantVirtualTimeBudget(5000);
- client_.DispatchProtocolMessage(mock_host_.get(), "{\"id\":0,\"result\":{}}");
+ client_.DispatchProtocolMessage(
+ mock_host_.get(), "{\"id\":0,\"result\":{\"virtualTimeBase\":1.0}}");
EXPECT_TRUE(set_up_complete_);
EXPECT_FALSE(budget_expired_);
@@ -98,13 +119,13 @@ namespace {
class MockTask : public VirtualTimeController::RepeatingTask {
public:
MOCK_METHOD2(IntervalElapsed,
- void(const base::TimeDelta& virtual_time,
+ void(base::TimeDelta virtual_time_offset,
const base::Callback<void()>& continue_callback));
MOCK_METHOD3(BudgetRequested,
- void(const base::TimeDelta& virtual_time,
- int requested_budget_ms,
+ void(base::TimeDelta virtual_time_offset,
+ base::TimeDelta requested_budget,
const base::Callback<void()>& continue_callback));
- MOCK_METHOD1(BudgetExpired, void(const base::TimeDelta& virtual_time));
+ MOCK_METHOD1(BudgetExpired, void(base::TimeDelta virtual_time_offset));
};
ACTION_TEMPLATE(RunClosure,
@@ -120,16 +141,19 @@ ACTION_P(RunClosure, closure) {
TEST_F(VirtualTimeControllerTest, InterleavesTasksWithVirtualTime) {
MockTask task;
- controller_->ScheduleRepeatingTask(&task, 1000);
+ controller_->ScheduleRepeatingTask(&task,
+ base::TimeDelta::FromMilliseconds(1000));
- EXPECT_CALL(task,
- BudgetRequested(base::TimeDelta::FromMilliseconds(0), 3000, _))
+ EXPECT_CALL(task, BudgetRequested(base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(3000), _))
.WillOnce(RunClosure<2>());
EXPECT_CALL(*mock_host_,
DispatchProtocolMessage(
&client_,
"{\"id\":0,\"method\":\"Emulation.setVirtualTimePolicy\","
- "\"params\":{\"budget\":1000,\"policy\":\"advance\"}}"))
+ "\"params\":{\"budget\":1000.0,"
+ "\"maxVirtualTimeTaskStarvationCount\":0,"
+ "\"policy\":\"advance\"}}"))
.WillOnce(Return(true));
GrantVirtualTimeBudget(3000);
@@ -137,7 +161,8 @@ TEST_F(VirtualTimeControllerTest, InterleavesTasksWithVirtualTime) {
EXPECT_FALSE(set_up_complete_);
EXPECT_FALSE(budget_expired_);
- client_.DispatchProtocolMessage(mock_host_.get(), "{\"id\":0,\"result\":{}}");
+ client_.DispatchProtocolMessage(
+ mock_host_.get(), "{\"id\":0,\"result\":{\"virtualTimeBase\":1.0}}");
EXPECT_TRUE(set_up_complete_);
EXPECT_FALSE(budget_expired_);
@@ -155,7 +180,9 @@ TEST_F(VirtualTimeControllerTest, InterleavesTasksWithVirtualTime) {
&client_,
base::StringPrintf(
"{\"id\":%d,\"method\":\"Emulation.setVirtualTimePolicy\","
- "\"params\":{\"budget\":1000,\"policy\":\"advance\"}}",
+ "\"params\":{\"budget\":1000.0,"
+ "\"maxVirtualTimeTaskStarvationCount\":0,"
+ "\"policy\":\"advance\"}}",
i * 2)))
.WillOnce(Return(true));
@@ -166,7 +193,8 @@ TEST_F(VirtualTimeControllerTest, InterleavesTasksWithVirtualTime) {
client_.DispatchProtocolMessage(
mock_host_.get(),
- base::StringPrintf("{\"id\":%d,\"result\":{}}", i * 2));
+ base::StringPrintf("{\"id\":%d,\"result\":{\"virtualTimeBase\":1.0}}",
+ i * 2));
EXPECT_FALSE(set_up_complete_);
EXPECT_FALSE(budget_expired_);
@@ -183,16 +211,19 @@ TEST_F(VirtualTimeControllerTest, InterleavesTasksWithVirtualTime) {
TEST_F(VirtualTimeControllerTest, CanceledTask) {
MockTask task;
- controller_->ScheduleRepeatingTask(&task, 1000);
+ controller_->ScheduleRepeatingTask(&task,
+ base::TimeDelta::FromMilliseconds(1000));
- EXPECT_CALL(task,
- BudgetRequested(base::TimeDelta::FromMilliseconds(0), 5000, _))
+ EXPECT_CALL(task, BudgetRequested(base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(5000), _))
.WillOnce(RunClosure<2>());
EXPECT_CALL(*mock_host_,
DispatchProtocolMessage(
&client_,
"{\"id\":0,\"method\":\"Emulation.setVirtualTimePolicy\","
- "\"params\":{\"budget\":1000,\"policy\":\"advance\"}}"))
+ "\"params\":{\"budget\":1000.0,"
+ "\"maxVirtualTimeTaskStarvationCount\":0,"
+ "\"policy\":\"advance\"}}"))
.WillOnce(Return(true));
GrantVirtualTimeBudget(5000);
@@ -200,7 +231,8 @@ TEST_F(VirtualTimeControllerTest, CanceledTask) {
EXPECT_FALSE(set_up_complete_);
EXPECT_FALSE(budget_expired_);
- client_.DispatchProtocolMessage(mock_host_.get(), "{\"id\":0,\"result\":{}}");
+ client_.DispatchProtocolMessage(
+ mock_host_.get(), "{\"id\":0,\"result\":{\"virtualTimeBase\":1.0}}");
EXPECT_TRUE(set_up_complete_);
EXPECT_FALSE(budget_expired_);
@@ -214,7 +246,9 @@ TEST_F(VirtualTimeControllerTest, CanceledTask) {
DispatchProtocolMessage(
&client_,
"{\"id\":2,\"method\":\"Emulation.setVirtualTimePolicy\","
- "\"params\":{\"budget\":1000,\"policy\":\"advance\"}}"))
+ "\"params\":{\"budget\":1000.0,"
+ "\"maxVirtualTimeTaskStarvationCount\":0,"
+ "\"policy\":\"advance\"}}"))
.WillOnce(Return(true));
SendVirtualTimeBudgetExpiredEvent();
@@ -223,7 +257,8 @@ TEST_F(VirtualTimeControllerTest, CanceledTask) {
EXPECT_FALSE(budget_expired_);
client_.DispatchProtocolMessage(
- mock_host_.get(), base::StringPrintf("{\"id\":2,\"result\":{}}"));
+ mock_host_.get(),
+ base::StringPrintf("{\"id\":2,\"result\":{\"virtualTimeBase\":1.0}}"));
EXPECT_FALSE(set_up_complete_);
EXPECT_FALSE(budget_expired_);
@@ -234,7 +269,9 @@ TEST_F(VirtualTimeControllerTest, CanceledTask) {
DispatchProtocolMessage(
&client_,
"{\"id\":4,\"method\":\"Emulation.setVirtualTimePolicy\","
- "\"params\":{\"budget\":3000,\"policy\":\"advance\"}}"))
+ "\"params\":{\"budget\":3000.0,"
+ "\"maxVirtualTimeTaskStarvationCount\":0,"
+ "\"policy\":\"advance\"}}"))
.WillOnce(Return(true));
SendVirtualTimeBudgetExpiredEvent();
@@ -243,7 +280,8 @@ TEST_F(VirtualTimeControllerTest, CanceledTask) {
EXPECT_FALSE(budget_expired_);
client_.DispatchProtocolMessage(
- mock_host_.get(), base::StringPrintf("{\"id\":4,\"result\":{}}"));
+ mock_host_.get(),
+ base::StringPrintf("{\"id\":4,\"result\":{\"virtualTimeBase\":1.0}}"));
EXPECT_FALSE(set_up_complete_);
EXPECT_FALSE(budget_expired_);
@@ -254,4 +292,181 @@ TEST_F(VirtualTimeControllerTest, CanceledTask) {
EXPECT_TRUE(budget_expired_);
}
+TEST_F(VirtualTimeControllerTest, MultipleTasks) {
+ MockTask task1;
+ MockTask task2;
+ controller_->ScheduleRepeatingTask(&task1,
+ base::TimeDelta::FromMilliseconds(1000));
+ controller_->ScheduleRepeatingTask(&task2,
+ base::TimeDelta::FromMilliseconds(1000));
+
+ EXPECT_CALL(task1,
+ BudgetRequested(base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(2000), _))
+ .WillOnce(RunClosure<2>());
+ EXPECT_CALL(task2,
+ BudgetRequested(base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(2000), _))
+ .WillOnce(RunClosure<2>());
+ // We should only get one call to Emulation.setVirtualTimePolicy despite
+ // having two tasks.
+ EXPECT_CALL(*mock_host_,
+ DispatchProtocolMessage(
+ &client_,
+ "{\"id\":0,\"method\":\"Emulation.setVirtualTimePolicy\","
+ "\"params\":{\"budget\":1000.0,"
+ "\"maxVirtualTimeTaskStarvationCount\":0,"
+ "\"policy\":\"advance\"}}"))
+ .WillOnce(Return(true));
+
+ GrantVirtualTimeBudget(2000);
+ EXPECT_FALSE(set_up_complete_);
+ EXPECT_FALSE(budget_expired_);
+
+ client_.DispatchProtocolMessage(
+ mock_host_.get(),
+ base::StringPrintf("{\"id\":0,\"result\":{\"virtualTimeBase\":1.0}}"));
+
+ EXPECT_TRUE(set_up_complete_);
+ EXPECT_FALSE(budget_expired_);
+}
+
+class VirtualTimeTask : public VirtualTimeController::RepeatingTask {
+ public:
+ using Task = base::Callback<void(base::TimeDelta virtual_time)>;
+
+ VirtualTimeTask(VirtualTimeController* controller,
+ Task budget_requested_task,
+ Task interval_elapsed_task,
+ Task budget_expired_task)
+ : controller_(controller),
+ budget_requested_task_(budget_requested_task),
+ interval_elapsed_task_(interval_elapsed_task),
+ budget_expired_task_(budget_expired_task) {}
+
+ void IntervalElapsed(
+ base::TimeDelta virtual_time,
+ const base::Callback<void()>& continue_callback) override {
+ interval_elapsed_task_.Run(virtual_time);
+ continue_callback.Run();
+ }
+
+ void BudgetRequested(
+ base::TimeDelta virtual_time,
+ base::TimeDelta requested_budget_ms,
+ const base::Callback<void()>& continue_callback) override {
+ budget_requested_task_.Run(virtual_time);
+ continue_callback.Run();
+ }
+
+ void BudgetExpired(base::TimeDelta virtual_time) override {
+ budget_expired_task_.Run(virtual_time);
+ };
+
+ VirtualTimeController* controller_; // NOT OWNED
+ Task budget_requested_task_;
+ Task interval_elapsed_task_;
+ Task budget_expired_task_;
+};
+
+TEST_F(VirtualTimeControllerTest, ReentrantTask) {
+#if defined(__clang__)
+ std::vector<std::string> log;
+ VirtualTimeTask task_b(
+ controller_.get(),
+ base::Bind(
+ [](std::vector<std::string>* log, base::TimeDelta virtual_time) {
+ log->push_back(base::StringPrintf(
+ "B: budget requested @ %d",
+ static_cast<int>(virtual_time.InMilliseconds())));
+ },
+ &log),
+ base::Bind(
+ [](std::vector<std::string>* log, VirtualTimeController* controller,
+ VirtualTimeTask* task_b, base::TimeDelta virtual_time) {
+ log->push_back(base::StringPrintf(
+ "B: interval elapsed @ %d",
+ static_cast<int>(virtual_time.InMilliseconds())));
+ controller->CancelRepeatingTask(task_b);
+ },
+ &log, controller_.get(), &task_b),
+ base::Bind(
+ [](std::vector<std::string>* log, base::TimeDelta virtual_time) {
+ log->push_back(base::StringPrintf(
+ "B: budget expired @ %d",
+ static_cast<int>(virtual_time.InMilliseconds())));
+ },
+ &log));
+
+ VirtualTimeTask task_a(
+ controller_.get(),
+ base::Bind(
+ [](std::vector<std::string>* log, base::TimeDelta virtual_time) {
+ log->push_back(base::StringPrintf(
+ "A: budget requested @ %d",
+ static_cast<int>(virtual_time.InMilliseconds())));
+ },
+ &log),
+ base::Bind(
+ [](std::vector<std::string>* log, VirtualTimeController* controller,
+ VirtualTimeTask* task_a, VirtualTimeTask* task_b,
+ base::TimeDelta virtual_time) {
+ log->push_back(base::StringPrintf(
+ "A: interval elapsed @ %d",
+ static_cast<int>(virtual_time.InMilliseconds())));
+ controller->CancelRepeatingTask(task_a);
+ controller->ScheduleRepeatingTask(
+ task_b, base::TimeDelta::FromMilliseconds(1500));
+ },
+ &log, controller_.get(), &task_a, &task_b),
+ base::Bind(
+ [](std::vector<std::string>* log, base::TimeDelta virtual_time) {
+ log->push_back(base::StringPrintf(
+ "A: budget expired @ %d",
+ static_cast<int>(virtual_time.InMilliseconds())));
+ },
+ &log));
+
+ controller_->ScheduleRepeatingTask(&task_a,
+ base::TimeDelta::FromMilliseconds(1000));
+
+ EXPECT_CALL(*mock_host_,
+ DispatchProtocolMessage(
+ &client_,
+ "{\"id\":0,\"method\":\"Emulation.setVirtualTimePolicy\","
+ "\"params\":{\"budget\":1000.0,"
+ "\"maxVirtualTimeTaskStarvationCount\":0,"
+ "\"policy\":\"advance\"}}"))
+ .WillOnce(Return(true));
+
+ GrantVirtualTimeBudget(6000);
+ Mock::VerifyAndClearExpectations(&mock_host_);
+
+ EXPECT_CALL(*mock_host_,
+ DispatchProtocolMessage(
+ &client_,
+ "{\"id\":2,\"method\":\"Emulation.setVirtualTimePolicy\","
+ "\"params\":{\"budget\":1500.0,"
+ "\"maxVirtualTimeTaskStarvationCount\":0,"
+ "\"policy\":\"advance\"}}"))
+ .WillOnce(Return(true));
+ SendVirtualTimeBudgetExpiredEvent();
+ Mock::VerifyAndClearExpectations(&mock_host_);
+
+ EXPECT_CALL(*mock_host_,
+ DispatchProtocolMessage(
+ &client_,
+ "{\"id\":4,\"method\":\"Emulation.setVirtualTimePolicy\","
+ "\"params\":{\"budget\":3500.0,"
+ "\"maxVirtualTimeTaskStarvationCount\":0,"
+ "\"policy\":\"advance\"}}"))
+ .WillOnce(Return(true));
+ SendVirtualTimeBudgetExpiredEvent();
+
+ EXPECT_THAT(
+ log, ElementsAre("A: budget requested @ 0", "A: interval elapsed @ 1000",
+ "B: interval elapsed @ 2500"));
+#endif
+}
+
} // namespace headless