diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-04-05 14:08:31 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-04-11 07:46:53 +0000 |
commit | 6a4cabb866f66d4128a97cdc6d9d08ce074f1247 (patch) | |
tree | ab00f70a5e89278d6a0d16ff0c42578dc4d84a2d /chromium/headless | |
parent | e733310db58160074f574c429d48f8308c0afe17 (diff) | |
download | qtwebengine-chromium-6a4cabb866f66d4128a97cdc6d9d08ce074f1247.tar.gz |
BASELINE: Update Chromium to 57.0.2987.144
Change-Id: I29db402ff696c71a04c4dbaec822c2e53efe0267
Reviewed-by: Peter Varga <pvarga@inf.u-szeged.hu>
Diffstat (limited to 'chromium/headless')
51 files changed, 1079 insertions, 450 deletions
diff --git a/chromium/headless/BUILD.gn b/chromium/headless/BUILD.gn index 9c9d392f436..d6cd10639cd 100644 --- a/chromium/headless/BUILD.gn +++ b/chromium/headless/BUILD.gn @@ -43,7 +43,7 @@ repack("pak") { } deps = [ - ":headless_lib_resources_grit", + ":resources", "//components/strings", "//content:resources", "//content/app/resources", @@ -60,7 +60,7 @@ repack("pak") { output = "$root_out_dir/headless_lib.pak" } -grit("headless_lib_resources_grit") { +grit("resources") { source = "lib/resources/headless_lib_resources.grd" outputs = [ "grit/headless_lib_resources.h", @@ -137,7 +137,7 @@ action("gen_devtools_client_api") { "lib/browser/devtools_api/domain_h.template", "lib/browser/devtools_api/domain_type_conversions_h.template", "lib/browser/devtools_api/domain_types_cc.template", - "lib/browser/devtools_api/domain_types_forward_declaration_h.template", + "lib/browser/devtools_api/domain_types_forward_declarations_h.template", "lib/browser/devtools_api/domain_types_h.template", ] @@ -167,6 +167,8 @@ static_library("headless_lib") { "lib/browser/headless_devtools_client_impl.h", "lib/browser/headless_devtools_manager_delegate.cc", "lib/browser/headless_devtools_manager_delegate.h", + "lib/browser/headless_platform_event_source.cc", + "lib/browser/headless_platform_event_source.h", "lib/browser/headless_screen.cc", "lib/browser/headless_screen.h", "lib/browser/headless_url_request_context_getter.cc", @@ -175,14 +177,12 @@ static_library("headless_lib") { "lib/browser/headless_web_contents_impl.h", "lib/browser/headless_window_parenting_client.cc", "lib/browser/headless_window_parenting_client.h", + "lib/browser/headless_window_tree_host.cc", + "lib/browser/headless_window_tree_host.h", "lib/headless_content_client.cc", "lib/headless_content_client.h", "lib/headless_content_main_delegate.cc", "lib/headless_content_main_delegate.h", - "lib/renderer/headless_content_renderer_client.cc", - "lib/renderer/headless_content_renderer_client.h", - "lib/utility/headless_content_utility_client.cc", - "lib/utility/headless_content_utility_client.h", "public/headless_browser.cc", "public/headless_browser.h", "public/headless_browser_context.h", @@ -232,11 +232,8 @@ static_library("headless_lib") { "//components/security_state/core", "//content/public/app:both", "//content/public/browser", - "//content/public/child", "//content/public/common", "//content/public/common:service_names", - "//content/public/renderer", - "//content/public/utility", "//net", "//services/service_manager/public/cpp", "//third_party/mesa:osmesa", @@ -244,10 +241,14 @@ static_library("headless_lib") { "//ui/base", "//ui/compositor", "//ui/display", - "//ui/ozone", + "//ui/events/devices", "//url", ] + if (use_ozone) { + deps += [ "//ui/ozone" ] + } + configs += [ ":headless_implementation" ] } @@ -331,6 +332,13 @@ test("headless_browsertests") { data = [ "$root_out_dir/headless_browser_tests.pak", + "$root_out_dir/headless_lib.pak", + "//net/tools/testserver/", + "//third_party/pyftpdlib/", + "//third_party/pywebsocket/", + "//third_party/skia/", + "//third_party/tlslite/", + "test/data/", ] defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ] diff --git a/chromium/headless/DEPS b/chromium/headless/DEPS index 500e0224908..edf61b6ede4 100644 --- a/chromium/headless/DEPS +++ b/chromium/headless/DEPS @@ -1,10 +1,15 @@ include_rules = [ - "+content/public", + "+content/public/app", + "+content/public/browser", + "+content/public/common", + "+content/public/test", "+mojo/public", "+net", "+ui/base", "+ui/base/resource", "+ui/display", + "+ui/events/devices", + "+ui/events/platform", "+ui/gfx", "+ui/gfx/geometry", "+ui/gl", diff --git a/chromium/headless/OWNERS b/chromium/headless/OWNERS index f845aaa8881..c87978e7381 100644 --- a/chromium/headless/OWNERS +++ b/chromium/headless/OWNERS @@ -1,2 +1,3 @@ skyostil@chromium.org alexclarke@chromium.org +altimin@chromium.org diff --git a/chromium/headless/README.md b/chromium/headless/README.md index 06128d79b66..bf4d30149cf 100644 --- a/chromium/headless/README.md +++ b/chromium/headless/README.md @@ -1,17 +1,37 @@ # Headless Chromium -Headless Chromium is a library for running Chromium in a headless/server -environment. Expected use cases include loading web pages, extracting metadata -(e.g., the DOM) and generating bitmaps from page contents -- using all the -modern web platform features provided by Chromium and Blink. +Headless Chromium allows running Chromium in a headless/server environment. +Expected use cases include loading web pages, extracting metadata (e.g., the +DOM) and generating bitmaps from page contents -- using all the modern web +platform features provided by Chromium and Blink. -See the [architecture design doc](https://docs.google.com/document/d/11zIkKkLBocofGgoTeeyibB2TZ_k7nR78v7kNelCatUE) -for more information. +There are two ways to use Headless Chromium: -## Headless shell +## Usage via the DevTools remote debugging protocol -The headless shell is a sample application which demonstrates the use of the -headless API. To run it, first initialize a headless build configuration: +1. Start a normal Chrome binary with the `--headless` command line flag +(Linux-only for now): + +``` +$ chrome --headless --remote-debugging-port=9222 https://chromium.org +``` + +Currently you'll also need to use `--disable-gpu` to avoid an error from a +missing Mesa library. + +2. Navigate to `http://localhost:9222` in another browser to open the +[DevTools](https://developer.chrome.com/devtools) interface or use a tool such +as [Selenium](http://www.seleniumhq.org/) to drive the headless browser. + +## Usage as a C++ library + +Headless Chromium can be built as a library for embedding into a C++ +application. This approach is otherwise similar to controlling the browser over +a DevTools connection, but it provides more customization points, e.g., for +networking and [mojo services](https://docs.google.com/document/d/1Fr6_DJH6OK9rG3-ibMvRPTNnHsAXPk0VzxxiuJDSK3M/edit#heading=h.qh0udvlk963d). + +Headless Shell is a sample application which demonstrates the use of the +headless C++ API. To run it, first initialize a headless build configuration: ``` $ mkdir -p out/Debug @@ -25,8 +45,7 @@ Then build the shell: $ ninja -C out/Debug headless_shell ``` -After the build completes, the headless shell can be run with the following -command: +After the build completes, Headless Shell can be run with the following command: ``` $ out/Debug/headless_shell https://www.google.com @@ -39,7 +58,7 @@ shell, start it with an argument specifying the debugging port: $ out/Debug/headless_shell --remote-debugging-port=9222 https://youtube.com ``` -Then navigate to `http://127.0.0.1:9222` with your browser. +Then navigate to `http://localhost:9222` with your browser. ## Embedder API @@ -67,8 +86,12 @@ web pages. Its main classes are: See the [client API documentation](https://docs.google.com/document/d/1rlqcp8nk-ZQvldNJWdbaMbwfDbJoOXvahPCDoPGOwhQ/edit#) for more information. -## Documentation +## Resources and Documentation + +Mailing list: [headless-dev@chromium.org](https://groups.google.com/a/chromium.org/forum/#!forum/headless-dev) +Bug tracker: [Proj=Headless](https://bugs.chromium.org/p/chromium/issues/list?can=2&q=Proj%3DHeadless) +* [Runtime headless mode for Chrome](https://docs.google.com/document/d/1aIJUzQr3eougZQp90bp4mqGr5gY6hdUice8UPa-Ys90/edit#) * [Virtual Time in Blink](https://docs.google.com/document/d/1y9kdt_zezt7pbey6uzvt1dgklwc1ob_vy4nzo1zbqmo/edit#heading=h.tn3gd1y9ifml) * [Headless Chrome architecture](https://docs.google.com/document/d/11zIkKkLBocofGgoTeeyibB2TZ_k7nR78v7kNelCatUE/edit) * [Headless Chrome C++ DevTools API](https://docs.google.com/document/d/1rlqcp8nk-ZQvldNJWdbaMbwfDbJoOXvahPCDoPGOwhQ/edit#heading=h.ng2bxb15li9a) @@ -77,3 +100,4 @@ web pages. Its main classes are: * [Controlling BeginFrame through DevTools](https://docs.google.com/document/d/1LVMYDkfjrrX9PNkrD8pJH5-Np_XUTQHIuJ8IEOirQH4/edit?ts=57d96dbd#heading=h.ndv831lc9uf0) * [Viewport bounds and scale for screenshots](https://docs.google.com/document/d/1VTcYz4q_x0f1O5IVrvRX4u1DVd_K34IVUl1VULLTCWw/edit#heading=h.ndv831lc9uf0) * [BlinkOn 6 presentation slides](https://docs.google.com/presentation/d/1gqK9F4lGAY3TZudAtdcxzMQNEE7PcuQrGu83No3l0lw/edit#slide=id.p) +* [Architecture design doc](https://docs.google.com/document/d/11zIkKkLBocofGgoTeeyibB2TZ_k7nR78v7kNelCatUE) diff --git a/chromium/headless/app/headless_shell.cc b/chromium/headless/app/headless_shell.cc index b57fc5f9070..047f408a0f4 100644 --- a/chromium/headless/app/headless_shell.cc +++ b/chromium/headless/app/headless_shell.cc @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include <iostream> #include <memory> +#include <sstream> #include <string> #include "base/base64.h" @@ -13,12 +13,15 @@ #include "base/files/file_path.h" #include "base/json/json_writer.h" #include "base/location.h" +#include "base/memory/ptr_util.h" #include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" #include "base/numerics/safe_conversions.h" #include "base/strings/string_number_conversions.h" #include "content/public/common/content_switches.h" #include "headless/app/headless_shell_switches.h" #include "headless/public/devtools/domains/emulation.h" +#include "headless/public/devtools/domains/inspector.h" #include "headless/public/devtools/domains/page.h" #include "headless/public/devtools/domains/runtime.h" #include "headless/public/headless_browser.h" @@ -42,7 +45,7 @@ const char kDefaultScreenshotFileName[] = "screenshot.png"; bool ParseWindowSize(std::string window_size, gfx::Size* parsed_window_size) { int width, height = 0; - if (sscanf(window_size.c_str(), "%dx%d", &width, &height) >= 2 && + if (sscanf(window_size.c_str(), "%d%*[x,]%d", &width, &height) >= 2 && width >= 0 && height >= 0) { parsed_window_size->set_width(width); parsed_window_size->set_height(height); @@ -55,6 +58,7 @@ bool ParseWindowSize(std::string window_size, gfx::Size* parsed_window_size) { // An application which implements a simple headless browser. class HeadlessShell : public HeadlessWebContents::Observer, emulation::ExperimentalObserver, + inspector::ExperimentalObserver, page::Observer { public: HeadlessShell() @@ -62,7 +66,8 @@ class HeadlessShell : public HeadlessWebContents::Observer, devtools_client_(HeadlessDevToolsClient::Create()), web_contents_(nullptr), processed_page_ready_(false), - browser_context_(nullptr) {} + browser_context_(nullptr), + weak_factory_(this) {} ~HeadlessShell() override {} void OnStart(HeadlessBrowser* browser) { @@ -70,6 +75,8 @@ class HeadlessShell : public HeadlessWebContents::Observer, HeadlessBrowserContext::Builder context_builder = browser_->CreateBrowserContextBuilder(); + // TODO(eseckler): These switches should also affect BrowserContexts that + // are created via DevTools later. if (base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kDeterministicFetch)) { deterministic_dispatcher_.reset( @@ -93,6 +100,7 @@ class HeadlessShell : public HeadlessWebContents::Observer, })); } browser_context_ = context_builder.Build(); + browser_->SetDefaultBrowserContext(browser_context_); HeadlessWebContents::Builder builder( browser_context_->CreateWebContentsBuilder()); @@ -101,16 +109,23 @@ class HeadlessShell : public HeadlessWebContents::Observer, // TODO(alexclarke): Should we navigate to about:blank first if using // virtual time? - if (!args.empty() && !args[0].empty()) - builder.SetInitialURL(GURL(args[0])); - - web_contents_ = builder.Build(); - if (!web_contents_) { - LOG(ERROR) << "Navigation failed"; - browser_->Shutdown(); - return; + if (args.empty()) + args.push_back("about:blank"); + for (auto it = args.rbegin(); it != args.rend(); ++it) { + GURL url(*it); + HeadlessWebContents* web_contents = builder.SetInitialURL(url).Build(); + if (!web_contents) { + LOG(ERROR) << "Navigation to " << url << " failed"; + browser_->Shutdown(); + return; + } + if (!web_contents_ && !RemoteDebuggingEnabled()) { + // TODO(jzfeng): Support observing multiple targets. + url_ = url; + web_contents_ = web_contents; + web_contents_->AddObserver(this); + } } - web_contents_->AddObserver(this); } void Shutdown() { @@ -118,8 +133,12 @@ class HeadlessShell : public HeadlessWebContents::Observer, return; if (!RemoteDebuggingEnabled()) { devtools_client_->GetEmulation()->GetExperimental()->RemoveObserver(this); + devtools_client_->GetInspector()->GetExperimental()->RemoveObserver(this); devtools_client_->GetPage()->RemoveObserver(this); - web_contents_->GetDevToolsTarget()->DetachClient(devtools_client_.get()); + if (web_contents_->GetDevToolsTarget()) { + web_contents_->GetDevToolsTarget()->DetachClient( + devtools_client_.get()); + } } web_contents_->RemoveObserver(this); web_contents_ = nullptr; @@ -129,9 +148,8 @@ class HeadlessShell : public HeadlessWebContents::Observer, // HeadlessWebContents::Observer implementation: void DevToolsTargetReady() override { - if (RemoteDebuggingEnabled()) - return; web_contents_->GetDevToolsTarget()->AttachClient(devtools_client_.get()); + devtools_client_->GetInspector()->GetExperimental()->AddObserver(this); devtools_client_->GetPage()->AddObserver(this); devtools_client_->GetPage()->Enable(); // Check if the document had already finished loading by the time we @@ -156,15 +174,41 @@ class HeadlessShell : public HeadlessWebContents::Observer, } else { PollReadyState(); } + + if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kTimeout)) { + std::string timeout_ms_ascii = + base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + switches::kTimeout); + int timeout_ms; + CHECK(base::StringToInt(timeout_ms_ascii, &timeout_ms)) + << "Expected an integer value for --timeout="; + browser_->BrowserMainThread()->PostDelayedTask( + FROM_HERE, + base::Bind(&HeadlessShell::FetchTimeout, weak_factory_.GetWeakPtr()), + base::TimeDelta::FromMilliseconds(timeout_ms)); + } + // TODO(skyostil): Implement more features to demonstrate the devtools API. } + void FetchTimeout() { + LOG(INFO) << "Timeout."; + devtools_client_->GetPage()->GetExperimental()->StopLoading( + page::StopLoadingParams::Builder().Build()); + } + + void OnTargetCrashed(const inspector::TargetCrashedParams& params) override { + LOG(ERROR) << "Abnormal renderer termination."; + // NB this never gets called if remote debugging is enabled. + Shutdown(); + } + void PollReadyState() { // We need to check the current location in addition to the ready state to // be sure the expected page is ready. devtools_client_->GetRuntime()->Evaluate( "document.readyState + ' ' + document.location.href", - base::Bind(&HeadlessShell::OnReadyState, base::Unretained(this))); + base::Bind(&HeadlessShell::OnReadyState, weak_factory_.GetWeakPtr())); } void OnReadyState(std::unique_ptr<runtime::EvaluateResult> result) { @@ -208,9 +252,8 @@ class HeadlessShell : public HeadlessWebContents::Observer, FetchDom(); } else if (base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kRepl)) { - std::cout - << "Type a Javascript expression to evaluate or \"quit\" to exit." - << std::endl; + LOG(INFO) + << "Type a Javascript expression to evaluate or \"quit\" to exit."; InputExpression(); } else if (base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kScreenshot)) { @@ -223,7 +266,7 @@ class HeadlessShell : public HeadlessWebContents::Observer, void FetchDom() { devtools_client_->GetRuntime()->Evaluate( "document.body.innerHTML", - base::Bind(&HeadlessShell::OnDomFetched, base::Unretained(this))); + base::Bind(&HeadlessShell::OnDomFetched, weak_factory_.GetWeakPtr())); } void OnDomFetched(std::unique_ptr<runtime::EvaluateResult> result) { @@ -233,7 +276,7 @@ class HeadlessShell : public HeadlessWebContents::Observer, } else { std::string dom; if (result->GetResult()->GetValue()->GetAsString(&dom)) { - std::cout << dom << std::endl; + printf("%s\n", dom.c_str()); } } Shutdown(); @@ -242,23 +285,29 @@ class HeadlessShell : public HeadlessWebContents::Observer, void InputExpression() { // Note that a real system should read user input asynchronously, because // otherwise all other browser activity is suspended (e.g., page loading). - std::string expression; - std::cout << ">>> "; - std::getline(std::cin, expression); - if (std::cin.bad() || std::cin.eof() || expression == "quit") { + printf(">>> "); + std::stringstream expression; + while (true) { + int c = fgetc(stdin); + if (c == EOF || c == '\n') { + break; + } + expression << c; + } + if (expression.str() == "quit") { Shutdown(); return; } devtools_client_->GetRuntime()->Evaluate( - expression, - base::Bind(&HeadlessShell::OnExpressionResult, base::Unretained(this))); + expression.str(), base::Bind(&HeadlessShell::OnExpressionResult, + weak_factory_.GetWeakPtr())); } void OnExpressionResult(std::unique_ptr<runtime::EvaluateResult> result) { std::unique_ptr<base::Value> value = result->Serialize(); std::string result_json; base::JSONWriter::Write(*value, &result_json); - std::cout << result_json << std::endl; + printf("%s\n", result_json.c_str()); InputExpression(); } @@ -266,7 +315,7 @@ class HeadlessShell : public HeadlessWebContents::Observer, devtools_client_->GetPage()->GetExperimental()->CaptureScreenshot( page::CaptureScreenshotParams::Builder().Build(), base::Bind(&HeadlessShell::OnScreenshotCaptured, - base::Unretained(this))); + weak_factory_.GetWeakPtr())); } void OnScreenshotCaptured( @@ -284,7 +333,7 @@ class HeadlessShell : public HeadlessWebContents::Observer, file_name, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE | base::File::FLAG_ASYNC, base::Bind(&HeadlessShell::OnScreenshotFileOpened, - base::Unretained(this), base::Passed(std::move(result)), + weak_factory_.GetWeakPtr(), base::Passed(std::move(result)), file_name)); if (open_result != net::ERR_IO_PENDING) { // Operation could not be started. @@ -311,7 +360,7 @@ class HeadlessShell : public HeadlessWebContents::Observer, const int write_result = screenshot_file_stream_->Write( buf.get(), buf->size(), base::Bind(&HeadlessShell::OnScreenshotFileWritten, - base::Unretained(this), file_name, buf->size())); + weak_factory_.GetWeakPtr(), file_name, buf->size())); if (write_result != net::ERR_IO_PENDING) { // Operation may have completed successfully or failed. OnScreenshotFileWritten(file_name, buf->size(), write_result); @@ -326,11 +375,11 @@ class HeadlessShell : public HeadlessWebContents::Observer, LOG(ERROR) << "Writing screenshot to file " << file_name.value() << " was unsuccessful: " << net::ErrorToString(write_result); } else { - std::cout << "Screenshot written to file " << file_name.value() << "." + LOG(INFO) << "Screenshot written to file " << file_name.value() << "." << std::endl; } int close_result = screenshot_file_stream_->Close(base::Bind( - &HeadlessShell::OnScreenshotFileClosed, base::Unretained(this))); + &HeadlessShell::OnScreenshotFileClosed, weak_factory_.GetWeakPtr())); if (close_result != net::ERR_IO_PENDING) { // Operation could not be started. OnScreenshotFileClosed(close_result); @@ -354,10 +403,46 @@ class HeadlessShell : public HeadlessWebContents::Observer, std::unique_ptr<net::FileStream> screenshot_file_stream_; HeadlessBrowserContext* browser_context_; std::unique_ptr<DeterministicDispatcher> deterministic_dispatcher_; + base::WeakPtrFactory<HeadlessShell> weak_factory_; DISALLOW_COPY_AND_ASSIGN(HeadlessShell); }; +bool ValidateCommandLine(const base::CommandLine& command_line) { + if (!command_line.HasSwitch(::switches::kRemoteDebuggingPort)) { + if (command_line.GetArgs().size() <= 1) + return true; + LOG(ERROR) << "Open multiple tabs is only supported when the " + << "remote debug port is set."; + return false; + } + if (command_line.HasSwitch(switches::kDumpDom)) { + LOG(ERROR) << "Dump DOM is disabled when remote debugging is enabled."; + return false; + } + if (command_line.HasSwitch(switches::kRepl)) { + LOG(ERROR) << "Evaluate Javascript is disabled " + << "when remote debugging is enabled."; + return false; + } + if (command_line.HasSwitch(switches::kScreenshot)) { + LOG(ERROR) << "Capture screenshot is disabled " + << "when remote debugging is enabled."; + return false; + } + if (command_line.HasSwitch(switches::kTimeout)) { + LOG(ERROR) << "Navigation timeout is disabled " + << "when remote debugging is enabled."; + return false; + } + if (command_line.HasSwitch(switches::kVirtualTimeBudget)) { + LOG(ERROR) << "Virtual time budget is disabled " + << "when remote debugging is enabled."; + return false; + } + return true; +} + int HeadlessShellMain(int argc, const char** argv) { RunChildProcessIfNeeded(argc, argv); HeadlessShell shell; @@ -365,6 +450,9 @@ int HeadlessShellMain(int argc, const char** argv) { // Enable devtools if requested. base::CommandLine command_line(argc, argv); + if (!ValidateCommandLine(command_line)) + return EXIT_FAILURE; + if (command_line.HasSwitch(::switches::kRemoteDebuggingPort)) { std::string address = kDevToolsHttpServerAddress; if (command_line.HasSwitch(switches::kRemoteDebuggingAddress)) { diff --git a/chromium/headless/app/headless_shell_switches.cc b/chromium/headless/app/headless_shell_switches.cc index 18cf95d3e1b..06750596296 100644 --- a/chromium/headless/app/headless_shell_switches.cc +++ b/chromium/headless/app/headless_shell_switches.cc @@ -35,6 +35,10 @@ const char kRepl[] = "repl"; // Save a screenshot of the loaded page. const char kScreenshot[] = "screenshot"; +// Issues a stop after the specified number of milliseconds. This cancels all +// navigation and causes the DOMContentLoaded event to fire. +const char kTimeout[] = "timeout"; + // Sets the GL implementation to use. Use a blank string to disable GL // rendering. const char kUseGL[] = "use-gl"; @@ -50,7 +54,7 @@ const char kUserDataDir[] = "user-data-dir"; // specified virtual time budget is exhausted. const char kVirtualTimeBudget[] = "virtual-time-budget"; -// Sets the initial window size. Provided as string in the format "800x600". +// Sets the initial window size. Provided as string in the format "800,600". const char kWindowSize[] = "window-size"; } // namespace switches diff --git a/chromium/headless/app/headless_shell_switches.h b/chromium/headless/app/headless_shell_switches.h index 3806b43232d..9d0a348e6e7 100644 --- a/chromium/headless/app/headless_shell_switches.h +++ b/chromium/headless/app/headless_shell_switches.h @@ -14,6 +14,7 @@ extern const char kProxyServer[]; extern const char kRemoteDebuggingAddress[]; extern const char kRepl[]; extern const char kScreenshot[]; +extern const char kTimeout[]; extern const char kUseGL[]; extern const char kUserDataDir[]; extern const char kVirtualTimeBudget[]; diff --git a/chromium/headless/lib/DEPS b/chromium/headless/lib/DEPS new file mode 100644 index 00000000000..54e541c3580 --- /dev/null +++ b/chromium/headless/lib/DEPS @@ -0,0 +1,6 @@ +specific_include_rules = { + "headless_web_contents_browsertest.cc": [ + "+third_party/skia/include", + ] +} + diff --git a/chromium/headless/lib/browser/devtools_api/client_api_generator.py b/chromium/headless/lib/browser/devtools_api/client_api_generator.py index ce96de211b4..435358af73d 100644 --- a/chromium/headless/lib/browser/devtools_api/client_api_generator.py +++ b/chromium/headless/lib/browser/devtools_api/client_api_generator.py @@ -494,8 +494,8 @@ def GenerateTypes(jinja_env, output_dirname, json_api): # Generate forward declarations for types. GeneratePerDomain( jinja_env, os.path.join(output_dirname, 'devtools', 'internal'), - json_api, 'domain_types_forward_declaration', ['h'], - lambda domain_name: 'types_forward_declaration_%s' % (domain_name, )) + json_api, 'domain_types_forward_declarations', ['h'], + lambda domain_name: 'types_forward_declarations_%s' % (domain_name, )) # Generate types on per-domain basis. GeneratePerDomain( jinja_env, os.path.join(output_dirname, 'devtools', 'domains'), diff --git a/chromium/headless/lib/browser/devtools_api/domain_cc.template b/chromium/headless/lib/browser/devtools_api/domain_cc.template index 88f28b1f56a..dc04d6e9d7b 100644 --- a/chromium/headless/lib/browser/devtools_api/domain_cc.template +++ b/chromium/headless/lib/browser/devtools_api/domain_cc.template @@ -102,6 +102,11 @@ void {{class_name}}::{{method_name}}(std::unique_ptr<{{method_name}}Params> para void Domain::Handle{{method_name}}Response(base::Callback<void(std::unique_ptr<{{method_name}}Result>)> callback, const base::Value& response) { if (callback.is_null()) return; + // This is an error response. + if (response.IsType(base::Value::Type::NONE)) { + callback.Run(nullptr); + return; + } ErrorReporter errors; std::unique_ptr<{{method_name}}Result> result = {{method_name}}Result::Parse(response, &errors); DCHECK(!errors.HasErrors()); diff --git a/chromium/headless/lib/browser/devtools_api/domain_types_forward_declaration_h.template b/chromium/headless/lib/browser/devtools_api/domain_types_forward_declarations_h.template index 4bb223b04ab..b321a17bc56 100644 --- a/chromium/headless/lib/browser/devtools_api/domain_types_forward_declaration_h.template +++ b/chromium/headless/lib/browser/devtools_api/domain_types_forward_declarations_h.template @@ -4,8 +4,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef HEADLESS_PUBLIC_DEVTOOLS_INTERNAL_TYPES_FORWARD_DECLARATION_{{domain.domain | camelcase_to_hacker_style | upper}}_H_ -#define HEADLESS_PUBLIC_DEVTOOLS_INTERNAL_TYPES_FORWARD_DECLARATION_{{domain.domain | camelcase_to_hacker_style | upper}}_H_ +#ifndef HEADLESS_PUBLIC_DEVTOOLS_INTERNAL_TYPES_FORWARD_DECLARATIONS_{{domain.domain | camelcase_to_hacker_style | upper}}_H_ +#define HEADLESS_PUBLIC_DEVTOOLS_INTERNAL_TYPES_FORWARD_DECLARATIONS_{{domain.domain | camelcase_to_hacker_style | upper}}_H_ #include "base/optional.h" #include "base/values.h" @@ -41,4 +41,4 @@ enum class {{type.id}} { } // namespace headless -#endif // HEADLESS_PUBLIC_DEVTOOLS_INTERNAL_TYPES_FORWARD_DECLARATION_{{domain.domain | camelcase_to_hacker_style | upper}}_H_ +#endif // HEADLESS_PUBLIC_DEVTOOLS_INTERNAL_TYPES_FORWARD_DECLARATIONS_{{domain.domain | camelcase_to_hacker_style | upper}}_H_ diff --git a/chromium/headless/lib/browser/devtools_api/domain_types_h.template b/chromium/headless/lib/browser/devtools_api/domain_types_h.template index f53f8ae1a52..b2e695ef6b9 100644 --- a/chromium/headless/lib/browser/devtools_api/domain_types_h.template +++ b/chromium/headless/lib/browser/devtools_api/domain_types_h.template @@ -10,7 +10,7 @@ #include "base/optional.h" #include "base/values.h" {% for domain_name in domain.dependencies %} -#include "headless/public/devtools/internal/types_forward_declaration_{{domain_name | camelcase_to_hacker_style}}.h" +#include "headless/public/devtools/internal/types_forward_declarations_{{domain_name | camelcase_to_hacker_style}}.h" {% endfor %} #include "headless/public/headless_export.h" #include "headless/public/util/error_reporter.h" diff --git a/chromium/headless/lib/browser/headless_browser_impl.cc b/chromium/headless/lib/browser/headless_browser_impl.cc index c1647257fd3..93d6918a76c 100644 --- a/chromium/headless/lib/browser/headless_browser_impl.cc +++ b/chromium/headless/lib/browser/headless_browser_impl.cc @@ -19,9 +19,11 @@ #include "headless/lib/browser/headless_browser_main_parts.h" #include "headless/lib/browser/headless_web_contents_impl.h" #include "headless/lib/browser/headless_window_parenting_client.h" +#include "headless/lib/browser/headless_window_tree_host.h" #include "headless/lib/headless_content_main_delegate.h" #include "ui/aura/env.h" -#include "ui/aura/window_tree_host.h" +#include "ui/aura/window.h" +#include "ui/events/devices/device_data_manager.h" #include "ui/gfx/geometry/size.h" namespace headless { @@ -52,6 +54,7 @@ HeadlessBrowserImpl::HeadlessBrowserImpl( : on_start_callback_(on_start_callback), options_(std::move(options)), browser_main_parts_(nullptr), + default_browser_context_(nullptr), weak_ptr_factory_(this) {} HeadlessBrowserImpl::~HeadlessBrowserImpl() {} @@ -119,9 +122,12 @@ void HeadlessBrowserImpl::set_browser_main_parts( void HeadlessBrowserImpl::RunOnStartCallback() { DCHECK(aura::Env::GetInstance()); + ui::DeviceDataManager::CreateInstance(); + window_tree_host_.reset( - aura::WindowTreeHost::Create(gfx::Rect(options()->window_size))); + new HeadlessWindowTreeHost(gfx::Rect(options()->window_size))); window_tree_host_->InitHost(); + window_tree_host_->window()->Show(); window_parenting_client_.reset( new HeadlessWindowParentingClient(window_tree_host_->window())); @@ -153,6 +159,19 @@ void HeadlessBrowserImpl::DestroyBrowserContext( auto it = browser_contexts_.find(browser_context->Id()); DCHECK(it != browser_contexts_.end()); browser_contexts_.erase(it); + if (default_browser_context_ == browser_context) + SetDefaultBrowserContext(nullptr); +} + +void HeadlessBrowserImpl::SetDefaultBrowserContext( + HeadlessBrowserContext* browser_context) { + DCHECK(!browser_context || + this == HeadlessBrowserContextImpl::From(browser_context)->browser()); + default_browser_context_ = browser_context; +} + +HeadlessBrowserContext* HeadlessBrowserImpl::GetDefaultBrowserContext() { + return default_browser_context_; } base::WeakPtr<HeadlessBrowserImpl> HeadlessBrowserImpl::GetWeakPtr() { diff --git a/chromium/headless/lib/browser/headless_browser_impl.h b/chromium/headless/lib/browser/headless_browser_impl.h index 74a4b6c29cf..f37dff1dbc4 100644 --- a/chromium/headless/lib/browser/headless_browser_impl.h +++ b/chromium/headless/lib/browser/headless_browser_impl.h @@ -51,6 +51,9 @@ class HeadlessBrowserImpl : public HeadlessBrowser { const std::string& devtools_agent_host_id) override; HeadlessBrowserContext* GetBrowserContextForId( const std::string& id) override; + void SetDefaultBrowserContext( + HeadlessBrowserContext* browser_context) override; + HeadlessBrowserContext* GetDefaultBrowserContext() override; void set_browser_main_parts(HeadlessBrowserMainParts* browser_main_parts); HeadlessBrowserMainParts* browser_main_parts() const; @@ -82,6 +85,7 @@ class HeadlessBrowserImpl : public HeadlessBrowser { std::unordered_map<std::string, std::unique_ptr<HeadlessBrowserContextImpl>> browser_contexts_; + HeadlessBrowserContext* default_browser_context_; // Not owned. base::WeakPtrFactory<HeadlessBrowserImpl> weak_ptr_factory_; diff --git a/chromium/headless/lib/browser/headless_browser_main_parts.h b/chromium/headless/lib/browser/headless_browser_main_parts.h index 97bc9e9e912..e29b92abed3 100644 --- a/chromium/headless/lib/browser/headless_browser_main_parts.h +++ b/chromium/headless/lib/browser/headless_browser_main_parts.h @@ -12,7 +12,6 @@ namespace headless { -class HeadlessBrowserContextImpl; class HeadlessBrowserImpl; class HeadlessBrowserMainParts : public content::BrowserMainParts { diff --git a/chromium/headless/lib/browser/headless_content_browser_client.cc b/chromium/headless/lib/browser/headless_content_browser_client.cc index 1bfe88ff8cb..8ba656c082c 100644 --- a/chromium/headless/lib/browser/headless_content_browser_client.cc +++ b/chromium/headless/lib/browser/headless_content_browser_client.cc @@ -61,7 +61,7 @@ HeadlessContentBrowserClient::GetDevToolsManagerDelegate() { std::unique_ptr<base::Value> HeadlessContentBrowserClient::GetServiceManifestOverlay( - const std::string& name) { + base::StringPiece name) { if (name != content::mojom::kBrowserServiceName || browser_->options()->mojo_service_names.empty()) return nullptr; diff --git a/chromium/headless/lib/browser/headless_content_browser_client.h b/chromium/headless/lib/browser/headless_content_browser_client.h index 8e9651e9d21..3f443819867 100644 --- a/chromium/headless/lib/browser/headless_content_browser_client.h +++ b/chromium/headless/lib/browser/headless_content_browser_client.h @@ -10,8 +10,6 @@ namespace headless { class HeadlessBrowserImpl; -class HeadlessBrowserMainParts; -class HeadlessDevToolsManagerDelegate; class HeadlessContentBrowserClient : public content::ContentBrowserClient { public: @@ -25,7 +23,7 @@ class HeadlessContentBrowserClient : public content::ContentBrowserClient { content::WebPreferences* prefs) override; content::DevToolsManagerDelegate* GetDevToolsManagerDelegate() override; std::unique_ptr<base::Value> GetServiceManifestOverlay( - const std::string& name) override; + base::StringPiece name) override; private: HeadlessBrowserImpl* browser_; // Not owned. diff --git a/chromium/headless/lib/browser/headless_devtools_client_impl.cc b/chromium/headless/lib/browser/headless_devtools_client_impl.cc index 833b9dc49af..b6e2905a405 100644 --- a/chromium/headless/lib/browser/headless_devtools_client_impl.cc +++ b/chromium/headless/lib/browser/headless_devtools_client_impl.cc @@ -32,6 +32,7 @@ HeadlessDevToolsClientImpl* HeadlessDevToolsClientImpl::From( HeadlessDevToolsClientImpl::HeadlessDevToolsClientImpl() : agent_host_(nullptr), next_message_id_(0), + renderer_crashed_(false), accessibility_domain_(this), animation_domain_(this), application_cache_domain_(this), @@ -68,17 +69,28 @@ HeadlessDevToolsClientImpl::HeadlessDevToolsClientImpl() HeadlessDevToolsClientImpl::~HeadlessDevToolsClientImpl() {} -void HeadlessDevToolsClientImpl::AttachToHost( +bool HeadlessDevToolsClientImpl::AttachToHost( + content::DevToolsAgentHost* agent_host) { + DCHECK(!agent_host_); + if (agent_host->AttachClient(this)) { + agent_host_ = agent_host; + return true; + } + return false; +} + +void HeadlessDevToolsClientImpl::ForceAttachToHost( content::DevToolsAgentHost* agent_host) { DCHECK(!agent_host_); agent_host_ = agent_host; - agent_host_->AttachClient(this); + agent_host_->ForceAttachClient(this); } void HeadlessDevToolsClientImpl::DetachFromHost( content::DevToolsAgentHost* agent_host) { DCHECK_EQ(agent_host_, agent_host); - agent_host_->DetachClient(this); + if (!renderer_crashed_) + agent_host_->DetachClient(this); agent_host_ = nullptr; pending_messages_.clear(); } @@ -114,11 +126,16 @@ bool HeadlessDevToolsClientImpl::DispatchMessageReply( pending_messages_.erase(it); if (!callback.callback_with_result.is_null()) { const base::DictionaryValue* result_dict; - if (!message_dict.GetDictionary("result", &result_dict)) { - NOTREACHED() << "Badly formed reply result"; + if (message_dict.GetDictionary("result", &result_dict)) { + callback.callback_with_result.Run(*result_dict); + } else if (message_dict.GetDictionary("error", &result_dict)) { + std::unique_ptr<base::Value> null_value = base::Value::CreateNullValue(); + DLOG(ERROR) << "Error in method call result: " << *result_dict; + callback.callback_with_result.Run(*null_value); + } else { + NOTREACHED() << "Reply has neither result nor error"; return false; } - callback.callback_with_result.Run(*result_dict); } else if (!callback.callback.is_null()) { callback.callback.Run(); } @@ -131,6 +148,8 @@ bool HeadlessDevToolsClientImpl::DispatchEvent( std::string method; if (!message_dict.GetString("method", &method)) return false; + if (method == "Inspector.targetCrashed") + renderer_crashed_ = true; EventHandlerMap::const_iterator it = event_handlers_.find(method); if (it == event_handlers_.end()) { NOTREACHED() << "Unknown event: " << method; @@ -292,6 +311,8 @@ template <typename CallbackType> void HeadlessDevToolsClientImpl::FinalizeAndSendMessage( base::DictionaryValue* message, CallbackType callback) { + if (renderer_crashed_) + return; DCHECK(agent_host_); int id = next_message_id_++; message->SetInteger("id", id); diff --git a/chromium/headless/lib/browser/headless_devtools_client_impl.h b/chromium/headless/lib/browser/headless_devtools_client_impl.h index 3ee800b13e8..a9e270a4b4f 100644 --- a/chromium/headless/lib/browser/headless_devtools_client_impl.h +++ b/chromium/headless/lib/browser/headless_devtools_client_impl.h @@ -115,7 +115,8 @@ class HeadlessDevToolsClientImpl : public HeadlessDevToolsClient, const char* method, base::Callback<void(const base::Value&)> callback) override; - void AttachToHost(content::DevToolsAgentHost* agent_host); + bool AttachToHost(content::DevToolsAgentHost* agent_host); + void ForceAttachToHost(content::DevToolsAgentHost* agent_host); void DetachFromHost(content::DevToolsAgentHost* agent_host); private: @@ -163,6 +164,8 @@ class HeadlessDevToolsClientImpl : public HeadlessDevToolsClient, EventHandlerMap event_handlers_; + bool renderer_crashed_; + accessibility::ExperimentalDomain accessibility_domain_; animation::ExperimentalDomain animation_domain_; application_cache::ExperimentalDomain application_cache_domain_; diff --git a/chromium/headless/lib/browser/headless_devtools_manager_delegate.cc b/chromium/headless/lib/browser/headless_devtools_manager_delegate.cc index 0c82f334ab8..0842569beb3 100644 --- a/chromium/headless/lib/browser/headless_devtools_manager_delegate.cc +++ b/chromium/headless/lib/browser/headless_devtools_manager_delegate.cc @@ -20,9 +20,57 @@ namespace headless { +namespace { +const char kIdParam[] = "id"; +const char kResultParam[] = "result"; +const char kErrorParam[] = "error"; +const char kErrorCodeParam[] = "code"; +const char kErrorMessageParam[] = "message"; + +// JSON RPC 2.0 spec: http://www.jsonrpc.org/specification#error_object +enum Error { + kErrorInvalidParam = -32602, + kErrorServerError = -32000 +}; + +std::unique_ptr<base::DictionaryValue> CreateSuccessResponse( + int command_id, + std::unique_ptr<base::Value> result) { + if (!result) + result.reset(new base::DictionaryValue()); + + std::unique_ptr<base::DictionaryValue> response(new base::DictionaryValue()); + response->SetInteger(kIdParam, command_id); + response->Set(kResultParam, std::move(result)); + return response; +} + +std::unique_ptr<base::DictionaryValue> CreateErrorResponse( + int command_id, + int error_code, + const std::string& error_message) { + std::unique_ptr<base::DictionaryValue> error_object( + new base::DictionaryValue()); + error_object->SetInteger(kErrorCodeParam, error_code); + error_object->SetString(kErrorMessageParam, error_message); + + std::unique_ptr<base::DictionaryValue> response(new base::DictionaryValue()); + response->Set(kErrorParam, std::move(error_object)); + return response; +} + +std::unique_ptr<base::DictionaryValue> CreateInvalidParamResponse( + int command_id, + const std::string& param) { + return CreateErrorResponse( + command_id, kErrorInvalidParam, + base::StringPrintf("Missing or invalid '%s' parameter", param.c_str())); +} +} // namespace + HeadlessDevToolsManagerDelegate::HeadlessDevToolsManagerDelegate( base::WeakPtr<HeadlessBrowserImpl> browser) - : browser_(std::move(browser)), default_browser_context_(nullptr) { + : browser_(std::move(browser)) { command_map_["Target.createTarget"] = &HeadlessDevToolsManagerDelegate::CreateTarget; command_map_["Target.closeTarget"] = @@ -45,29 +93,25 @@ base::DictionaryValue* HeadlessDevToolsManagerDelegate::HandleCommand( int id; std::string method; - const base::DictionaryValue* params = nullptr; if (!command->GetInteger("id", &id) || - !command->GetString("method", &method) || - !command->GetDictionary("params", ¶ms)) { + !command->GetString("method", &method)) { return nullptr; } auto find_it = command_map_.find(method); if (find_it == command_map_.end()) return nullptr; CommandMemberFnPtr command_fn_ptr = find_it->second; - std::unique_ptr<base::Value> cmd_result(((this)->*command_fn_ptr)(params)); - if (!cmd_result) - return nullptr; - - std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue()); - result->SetInteger("id", id); - result->Set("result", std::move(cmd_result)); - return result.release(); + const base::DictionaryValue* params = nullptr; + command->GetDictionary("params", ¶ms); + std::unique_ptr<base::DictionaryValue> cmd_result( + ((this)->*command_fn_ptr)(id, params)); + return cmd_result.release(); } std::string HeadlessDevToolsManagerDelegate::GetDiscoveryPageHTML() { - return ResourceBundle::GetSharedInstance().GetRawDataResource( - IDR_HEADLESS_LIB_DEVTOOLS_DISCOVERY_PAGE).as_string(); + return ResourceBundle::GetSharedInstance() + .GetRawDataResource(IDR_HEADLESS_LIB_DEVTOOLS_DISCOVERY_PAGE) + .as_string(); } std::string HeadlessDevToolsManagerDelegate::GetFrontendResource( @@ -75,26 +119,34 @@ std::string HeadlessDevToolsManagerDelegate::GetFrontendResource( return content::DevToolsFrontendHost::GetFrontendResource(path).as_string(); } -std::unique_ptr<base::Value> HeadlessDevToolsManagerDelegate::CreateTarget( +std::unique_ptr<base::DictionaryValue> +HeadlessDevToolsManagerDelegate::CreateTarget( + int command_id, const base::DictionaryValue* params) { std::string url; std::string browser_context_id; int width = browser_->options()->window_size.width(); int height = browser_->options()->window_size.height(); - params->GetString("url", &url); + if (!params || !params->GetString("url", &url)) + return CreateInvalidParamResponse(command_id, "url"); params->GetString("browserContextId", &browser_context_id); params->GetInteger("width", &width); params->GetInteger("height", &height); - // TODO(alexclarke): Should we fail when user passes incorrect id? HeadlessBrowserContext* context = browser_->GetBrowserContextForId(browser_context_id); - if (!context) { - if (!default_browser_context_) { - default_browser_context_ = - browser_->CreateBrowserContextBuilder().Build(); + if (!browser_context_id.empty()) { + context = browser_->GetBrowserContextForId(browser_context_id); + if (!context) + return CreateInvalidParamResponse(command_id, "browserContextId"); + } else { + context = browser_->GetDefaultBrowserContext(); + if (!context) { + return CreateErrorResponse(command_id, kErrorServerError, + "You specified no |browserContextId|, but " + "there is no default browser context set on " + "HeadlessBrowser"); } - context = default_browser_context_; } HeadlessWebContentsImpl* web_contents_impl = @@ -103,18 +155,21 @@ std::unique_ptr<base::Value> HeadlessDevToolsManagerDelegate::CreateTarget( .SetWindowSize(gfx::Size(width, height)) .Build()); - return target::CreateTargetResult::Builder() - .SetTargetId(web_contents_impl->GetDevToolsAgentHostId()) - .Build() - ->Serialize(); + std::unique_ptr<base::Value> result( + target::CreateTargetResult::Builder() + .SetTargetId(web_contents_impl->GetDevToolsAgentHostId()) + .Build() + ->Serialize()); + return CreateSuccessResponse(command_id, std::move(result)); } -std::unique_ptr<base::Value> HeadlessDevToolsManagerDelegate::CloseTarget( +std::unique_ptr<base::DictionaryValue> +HeadlessDevToolsManagerDelegate::CloseTarget( + int command_id, const base::DictionaryValue* params) { std::string target_id; - if (!params->GetString("targetId", &target_id)) { - return nullptr; - } + if (!params || !params->GetString("targetId", &target_id)) + return CreateInvalidParamResponse(command_id, "targetId"); HeadlessWebContents* web_contents = browser_->GetWebContentsForDevToolsAgentHostId(target_id); bool success = false; @@ -122,46 +177,51 @@ std::unique_ptr<base::Value> HeadlessDevToolsManagerDelegate::CloseTarget( web_contents->Close(); success = true; } - return target::CloseTargetResult::Builder() - .SetSuccess(success) - .Build() - ->Serialize(); + std::unique_ptr<base::Value> result(target::CloseTargetResult::Builder() + .SetSuccess(success) + .Build() + ->Serialize()); + return CreateSuccessResponse(command_id, std::move(result)); } -std::unique_ptr<base::Value> +std::unique_ptr<base::DictionaryValue> HeadlessDevToolsManagerDelegate::CreateBrowserContext( + int command_id, const base::DictionaryValue* params) { HeadlessBrowserContext* browser_context = browser_->CreateBrowserContextBuilder().Build(); - std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue()); - return target::CreateBrowserContextResult::Builder() - .SetBrowserContextId(browser_context->Id()) - .Build() - ->Serialize(); + std::unique_ptr<base::Value> result( + target::CreateBrowserContextResult::Builder() + .SetBrowserContextId(browser_context->Id()) + .Build() + ->Serialize()); + return CreateSuccessResponse(command_id, std::move(result)); } -std::unique_ptr<base::Value> +std::unique_ptr<base::DictionaryValue> HeadlessDevToolsManagerDelegate::DisposeBrowserContext( + int command_id, const base::DictionaryValue* params) { std::string browser_context_id; - if (!params->GetString("browserContextId", &browser_context_id)) { - return nullptr; - } + if (!params || !params->GetString("browserContextId", &browser_context_id)) + return CreateInvalidParamResponse(command_id, "browserContextId"); HeadlessBrowserContext* context = browser_->GetBrowserContextForId(browser_context_id); bool success = false; - if (context && context != default_browser_context_ && + if (context && context != browser_->GetDefaultBrowserContext() && context->GetAllWebContents().empty()) { success = true; context->Close(); } - return target::DisposeBrowserContextResult::Builder() - .SetSuccess(success) - .Build() - ->Serialize(); + std::unique_ptr<base::Value> result( + target::DisposeBrowserContextResult::Builder() + .SetSuccess(success) + .Build() + ->Serialize()); + return CreateSuccessResponse(command_id, std::move(result)); } } // namespace headless diff --git a/chromium/headless/lib/browser/headless_devtools_manager_delegate.h b/chromium/headless/lib/browser/headless_devtools_manager_delegate.h index 4d43fc76b79..c891e8246c6 100644 --- a/chromium/headless/lib/browser/headless_devtools_manager_delegate.h +++ b/chromium/headless/lib/browser/headless_devtools_manager_delegate.h @@ -16,8 +16,6 @@ namespace headless { class HeadlessBrowserImpl; -class HeadlessBrowserContext; -class HeadlessWebContentsImpl; class HeadlessDevToolsManagerDelegate : public content::DevToolsManagerDelegate { @@ -33,22 +31,26 @@ class HeadlessDevToolsManagerDelegate std::string GetFrontendResource(const std::string& path) override; private: - std::unique_ptr<base::Value> CreateTarget( + std::unique_ptr<base::DictionaryValue> CreateTarget( + int command_id, const base::DictionaryValue* params); - std::unique_ptr<base::Value> CloseTarget(const base::DictionaryValue* params); - std::unique_ptr<base::Value> CreateBrowserContext( + std::unique_ptr<base::DictionaryValue> CloseTarget( + int command_id, const base::DictionaryValue* params); - std::unique_ptr<base::Value> DisposeBrowserContext( + std::unique_ptr<base::DictionaryValue> CreateBrowserContext( + int command_id, + const base::DictionaryValue* params); + std::unique_ptr<base::DictionaryValue> DisposeBrowserContext( + int command_id, const base::DictionaryValue* params); base::WeakPtr<HeadlessBrowserImpl> browser_; - using CommandMemberFnPtr = std::unique_ptr<base::Value> ( - HeadlessDevToolsManagerDelegate::*)(const base::DictionaryValue* params); + using CommandMemberFnPtr = std::unique_ptr<base::DictionaryValue> ( + HeadlessDevToolsManagerDelegate::*)(int command_id, + const base::DictionaryValue* params); std::map<std::string, CommandMemberFnPtr> command_map_; - - HeadlessBrowserContext* default_browser_context_; }; } // namespace headless diff --git a/chromium/headless/lib/browser/headless_platform_event_source.cc b/chromium/headless/lib/browser/headless_platform_event_source.cc new file mode 100644 index 00000000000..7c3100ee4f2 --- /dev/null +++ b/chromium/headless/lib/browser/headless_platform_event_source.cc @@ -0,0 +1,13 @@ +// Copyright 2016 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/lib/browser/headless_platform_event_source.h" + +namespace headless { + +HeadlessPlatformEventSource::HeadlessPlatformEventSource() {} + +HeadlessPlatformEventSource::~HeadlessPlatformEventSource() {} + +} // namespace headless diff --git a/chromium/headless/lib/browser/headless_platform_event_source.h b/chromium/headless/lib/browser/headless_platform_event_source.h new file mode 100644 index 00000000000..ccd74632567 --- /dev/null +++ b/chromium/headless/lib/browser/headless_platform_event_source.h @@ -0,0 +1,26 @@ +// Copyright 2016 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_LIB_BROWSER_HEADLESS_PLATFORM_EVENT_SOURCE_H_ +#define HEADLESS_LIB_BROWSER_HEADLESS_PLATFORM_EVENT_SOURCE_H_ + +#include "base/macros.h" +#include "ui/events/platform/platform_event_source.h" + +namespace headless { + +// A stub event source whose main purpose is to prevent the default platform +// event source from getting created. +class HeadlessPlatformEventSource : public ui::PlatformEventSource { + public: + HeadlessPlatformEventSource(); + ~HeadlessPlatformEventSource() override; + + private: + DISALLOW_COPY_AND_ASSIGN(HeadlessPlatformEventSource); +}; + +} // namespace headless + +#endif // HEADLESS_LIB_BROWSER_HEADLESS_PLATFORM_EVENT_SOURCE_H_ diff --git a/chromium/headless/lib/browser/headless_screen.cc b/chromium/headless/lib/browser/headless_screen.cc index be86924d2e3..9cc1e4745a5 100644 --- a/chromium/headless/lib/browser/headless_screen.cc +++ b/chromium/headless/lib/browser/headless_screen.cc @@ -43,6 +43,7 @@ aura::WindowTreeHost* HeadlessScreen::CreateHostForPrimaryDisplay() { host_->GetInputMethod()->OnFocus(); host_->window()->AddObserver(this); host_->InitHost(); + host_->window()->Show(); return host_; } diff --git a/chromium/headless/lib/browser/headless_screen.h b/chromium/headless/lib/browser/headless_screen.h index ed9e388f079..8c5cb98c083 100644 --- a/chromium/headless/lib/browser/headless_screen.h +++ b/chromium/headless/lib/browser/headless_screen.h @@ -19,7 +19,6 @@ class Transform; namespace aura { class Window; -class WindowParentingClient; class WindowTreeHost; } 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 04a04d5c25a..0bb623df377 100644 --- a/chromium/headless/lib/browser/headless_url_request_context_getter.cc +++ b/chromium/headless/lib/browser/headless_url_request_context_getter.cc @@ -88,14 +88,7 @@ HeadlessURLRequestContextGetter::GetURLRequestContext() { base::WrapUnique(pair.second.release())); } protocol_handlers_.clear(); - - std::vector<std::unique_ptr<net::URLRequestInterceptor>> - request_interceptors; - for (auto it : request_interceptors_) { - request_interceptors.push_back(base::WrapUnique(it)); - } - request_interceptors_.weak_clear(); - builder.SetInterceptors(std::move(request_interceptors)); + builder.SetInterceptors(std::move(request_interceptors_)); url_request_context_ = builder.Build(); } 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 166f6a9cbe6..e1bee1ea43f 100644 --- a/chromium/headless/lib/browser/headless_url_request_context_getter.h +++ b/chromium/headless/lib/browser/headless_url_request_context_getter.h @@ -19,18 +19,9 @@ #include "net/url_request/url_request_context_getter.h" #include "net/url_request/url_request_job_factory.h" -namespace base { -class MessageLoop; -} - namespace net { class HostResolver; -class MappedHostResolver; -class NetworkDelegate; -class NetLog; class ProxyConfigService; -class ProxyService; -class URLRequestContextStorage; } namespace headless { diff --git a/chromium/headless/lib/browser/headless_web_contents_impl.cc b/chromium/headless/lib/browser/headless_web_contents_impl.cc index dfdf31b30db..1516d964ff3 100644 --- a/chromium/headless/lib/browser/headless_web_contents_impl.cc +++ b/chromium/headless/lib/browser/headless_web_contents_impl.cc @@ -26,7 +26,6 @@ #include "content/public/browser/web_contents_delegate.h" #include "content/public/common/bindings_policy.h" #include "content/public/common/origin_util.h" -#include "content/public/renderer/render_frame.h" #include "headless/lib/browser/headless_browser_context_impl.h" #include "headless/lib/browser/headless_browser_impl.h" #include "headless/lib/browser/headless_browser_main_parts.h" @@ -57,6 +56,8 @@ class WebContentsObserverAdapter : public content::WebContentsObserver { observer_->DevToolsTargetReady(); } + HeadlessWebContents::Observer* observer() { return observer_; } + private: HeadlessWebContents::Observer* observer_; // Not owned. @@ -157,12 +158,16 @@ HeadlessWebContentsImpl::HeadlessWebContentsImpl( new HeadlessWebContentsImpl::Delegate(browser_context)), web_contents_(web_contents), agent_host_(content::DevToolsAgentHost::GetOrCreateFor(web_contents)), - browser_context_(browser_context) { + browser_context_(browser_context), + render_process_host_(web_contents->GetRenderProcessHost()) { web_contents_->SetDelegate(web_contents_delegate_.get()); + render_process_host_->AddObserver(this); } HeadlessWebContentsImpl::~HeadlessWebContentsImpl() { web_contents_->Close(); + if (render_process_host_) + render_process_host_->RemoveObserver(this); } void HeadlessWebContentsImpl::RenderFrameCreated( @@ -214,12 +219,35 @@ void HeadlessWebContentsImpl::RemoveObserver(Observer* observer) { observer_map_.erase(it); } +void HeadlessWebContentsImpl::RenderProcessExited( + content::RenderProcessHost* host, + base::TerminationStatus status, + int exit_code) { + DCHECK_EQ(render_process_host_, host); + for (const auto& pair : observer_map_) { + pair.second->observer()->RenderProcessExited(status, exit_code); + } +} + +void HeadlessWebContentsImpl::RenderProcessHostDestroyed( + content::RenderProcessHost* host) { + DCHECK_EQ(render_process_host_, host); + render_process_host_ = nullptr; +} + HeadlessDevToolsTarget* HeadlessWebContentsImpl::GetDevToolsTarget() { return web_contents()->GetMainFrame()->IsRenderFrameLive() ? this : nullptr; } -void HeadlessWebContentsImpl::AttachClient(HeadlessDevToolsClient* client) { - HeadlessDevToolsClientImpl::From(client)->AttachToHost(agent_host_.get()); +bool HeadlessWebContentsImpl::AttachClient(HeadlessDevToolsClient* client) { + return HeadlessDevToolsClientImpl::From(client)->AttachToHost( + agent_host_.get()); +} + +void HeadlessWebContentsImpl::ForceAttachClient( + HeadlessDevToolsClient* client) { + HeadlessDevToolsClientImpl::From(client)->ForceAttachToHost( + agent_host_.get()); } void HeadlessWebContentsImpl::DetachClient(HeadlessDevToolsClient* client) { @@ -227,6 +255,11 @@ void HeadlessWebContentsImpl::DetachClient(HeadlessDevToolsClient* client) { HeadlessDevToolsClientImpl::From(client)->DetachFromHost(agent_host_.get()); } +bool HeadlessWebContentsImpl::IsAttached() { + DCHECK(agent_host_); + return agent_host_->IsAttached(); +} + content::WebContents* HeadlessWebContentsImpl::web_contents() const { return web_contents_.get(); } diff --git a/chromium/headless/lib/browser/headless_web_contents_impl.h b/chromium/headless/lib/browser/headless_web_contents_impl.h index 43293847b35..a18a46aa49d 100644 --- a/chromium/headless/lib/browser/headless_web_contents_impl.h +++ b/chromium/headless/lib/browser/headless_web_contents_impl.h @@ -10,6 +10,7 @@ #include <string> #include <unordered_map> +#include "content/public/browser/render_process_host_observer.h" #include "content/public/browser/web_contents_observer.h" #include "headless/public/headless_devtools_target.h" #include "headless/public/headless_web_contents.h" @@ -19,7 +20,6 @@ class Window; } namespace content { -class BrowserContext; class DevToolsAgentHost; class WebContents; } @@ -29,12 +29,12 @@ class Size; } namespace headless { -class HeadlessDevToolsHostImpl; class HeadlessBrowserImpl; class WebContentsObserverAdapter; class HeadlessWebContentsImpl : public HeadlessWebContents, public HeadlessDevToolsTarget, + public content::RenderProcessHostObserver, public content::WebContentsObserver { public: ~HeadlessWebContentsImpl() override; @@ -56,8 +56,16 @@ class HeadlessWebContentsImpl : public HeadlessWebContents, HeadlessDevToolsTarget* GetDevToolsTarget() override; // HeadlessDevToolsTarget implementation: - void AttachClient(HeadlessDevToolsClient* client) override; + bool AttachClient(HeadlessDevToolsClient* client) override; + void ForceAttachClient(HeadlessDevToolsClient* client) override; void DetachClient(HeadlessDevToolsClient* client) override; + bool IsAttached() override; + + // RenderProcessHostObserver implementation: + void RenderProcessExited(content::RenderProcessHost* host, + base::TerminationStatus status, + int exit_code) override; + void RenderProcessHostDestroyed(content::RenderProcessHost* host) override; // content::WebContentsObserver implementation: void RenderFrameCreated(content::RenderFrameHost* render_frame_host) override; @@ -88,7 +96,8 @@ class HeadlessWebContentsImpl : public HeadlessWebContents, scoped_refptr<content::DevToolsAgentHost> agent_host_; std::list<MojoService> mojo_services_; - HeadlessBrowserContextImpl* browser_context_; // Not owned. + HeadlessBrowserContextImpl* browser_context_; // Not owned. + content::RenderProcessHost* render_process_host_; // Not owned. using ObserverMap = std::unordered_map<HeadlessWebContents::Observer*, diff --git a/chromium/headless/lib/browser/headless_window_tree_host.cc b/chromium/headless/lib/browser/headless_window_tree_host.cc new file mode 100644 index 00000000000..96cec1a96e6 --- /dev/null +++ b/chromium/headless/lib/browser/headless_window_tree_host.cc @@ -0,0 +1,75 @@ +// Copyright 2016 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/lib/browser/headless_window_tree_host.h" + +#include "ui/gfx/icc_profile.h" + +namespace headless { + +HeadlessWindowTreeHost::HeadlessWindowTreeHost(const gfx::Rect& bounds) + : bounds_(bounds) { + CreateCompositor(); + OnAcceleratedWidgetAvailable(); +} + +HeadlessWindowTreeHost::~HeadlessWindowTreeHost() { + DestroyCompositor(); + DestroyDispatcher(); +} + +bool HeadlessWindowTreeHost::CanDispatchEvent(const ui::PlatformEvent& event) { + return false; +} + +uint32_t HeadlessWindowTreeHost::DispatchEvent(const ui::PlatformEvent& event) { + return 0; +} + +ui::EventSource* HeadlessWindowTreeHost::GetEventSource() { + return this; +} + +gfx::AcceleratedWidget HeadlessWindowTreeHost::GetAcceleratedWidget() { + return gfx::AcceleratedWidget(); +} + +gfx::Rect HeadlessWindowTreeHost::GetBoundsInPixels() const { + return bounds_; +} + +void HeadlessWindowTreeHost::SetBoundsInPixels(const gfx::Rect& bounds) { + bool origin_changed = bounds_.origin() != bounds.origin(); + bool size_changed = bounds_.size() != bounds.size(); + bounds_ = bounds; + if (origin_changed) + OnHostMovedInPixels(bounds.origin()); + if (size_changed) + OnHostResizedInPixels(bounds.size()); +} + +void HeadlessWindowTreeHost::ShowImpl() {} + +void HeadlessWindowTreeHost::HideImpl() {} + +gfx::Point HeadlessWindowTreeHost::GetLocationOnScreenInPixels() const { + return gfx::Point(); +} + +void HeadlessWindowTreeHost::SetCapture() {} + +void HeadlessWindowTreeHost::ReleaseCapture() {} + +void HeadlessWindowTreeHost::SetCursorNative(gfx::NativeCursor cursor_type) {} + +void HeadlessWindowTreeHost::MoveCursorToScreenLocationInPixels( + const gfx::Point& location) {} + +void HeadlessWindowTreeHost::OnCursorVisibilityChangedNative(bool show) {} + +gfx::ICCProfile HeadlessWindowTreeHost::GetICCProfileForCurrentDisplay() { + return gfx::ICCProfile(); +} + +} // namespace headless diff --git a/chromium/headless/lib/browser/headless_window_tree_host.h b/chromium/headless/lib/browser/headless_window_tree_host.h new file mode 100644 index 00000000000..c01545c9c35 --- /dev/null +++ b/chromium/headless/lib/browser/headless_window_tree_host.h @@ -0,0 +1,48 @@ +// Copyright 2016 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_LIB_BROWSER_HEADLESS_WINDOW_TREE_HOST_H_ +#define HEADLESS_LIB_BROWSER_HEADLESS_WINDOW_TREE_HOST_H_ + +#include "base/macros.h" +#include "ui/aura/window_tree_host.h" +#include "ui/events/platform/platform_event_dispatcher.h" +#include "ui/gfx/geometry/rect.h" + +namespace headless { + +class HeadlessWindowTreeHost : public aura::WindowTreeHost, + public ui::PlatformEventDispatcher { + public: + explicit HeadlessWindowTreeHost(const gfx::Rect& bounds); + ~HeadlessWindowTreeHost() override; + + // ui::PlatformEventDispatcher: + bool CanDispatchEvent(const ui::PlatformEvent& event) override; + uint32_t DispatchEvent(const ui::PlatformEvent& event) override; + + // WindowTreeHost: + ui::EventSource* GetEventSource() override; + gfx::AcceleratedWidget GetAcceleratedWidget() override; + void ShowImpl() override; + void HideImpl() override; + gfx::Rect GetBoundsInPixels() const override; + void SetBoundsInPixels(const gfx::Rect& bounds) override; + gfx::Point GetLocationOnScreenInPixels() const override; + void SetCapture() override; + void ReleaseCapture() override; + void SetCursorNative(gfx::NativeCursor cursor_type) override; + void MoveCursorToScreenLocationInPixels(const gfx::Point& location) override; + void OnCursorVisibilityChangedNative(bool show) override; + gfx::ICCProfile GetICCProfileForCurrentDisplay() override; + + private: + gfx::Rect bounds_; + + DISALLOW_COPY_AND_ASSIGN(HeadlessWindowTreeHost); +}; + +} // namespace headless + +#endif // HEADLESS_LIB_BROWSER_HEADLESS_WINDOW_TREE_HOST_H_ diff --git a/chromium/headless/lib/embedder_mojo_browsertest.cc b/chromium/headless/lib/embedder_mojo_browsertest.cc index 3fb4ed1a5f5..92800f1acc3 100644 --- a/chromium/headless/lib/embedder_mojo_browsertest.cc +++ b/chromium/headless/lib/embedder_mojo_browsertest.cc @@ -152,16 +152,12 @@ class MojoBindingsTest : public EmbedderMojoTest { "// fires after the requested modules have been loaded. \n" "define([ \n" " 'headless/lib/embedder_test.mojom', \n" - " 'mojo/public/js/core', \n" - " 'mojo/public/js/router', \n" " 'content/public/renderer/frame_interfaces', \n" - " ], function(embedderMojom, mojoCore, routerModule, \n" - " frameInterfaces) { \n" + " ], function(embedderMojom, frameInterfaces) { \n" " var testEmbedderService = \n" - " new embedderMojom.TestEmbedderService.proxyClass( \n" - " new routerModule.Router( \n" - " frameInterfaces.getInterface( \n" - " embedderMojom.TestEmbedderService.name))); \n" + " new embedderMojom.TestEmbedderServicePtr( \n" + " frameInterfaces.getInterface( \n" + " embedderMojom.TestEmbedderService.name)); \n" " \n" " // Send a message to the embedder! \n" " testEmbedderService.returnTestResult('hello world'); \n" @@ -238,7 +234,7 @@ class HttpDisabledByDefaultWhenMojoBindingsUsed : public EmbedderMojoTest, } void ReturnTestResult(const std::string& result) override { - FinishAsynchronousTest(); + DisableClientAndFinishAsynchronousTest(); FAIL() << "The HTTP page should not have been served and we should not have" " recieved a mojo callback!"; } @@ -246,6 +242,12 @@ class HttpDisabledByDefaultWhenMojoBindingsUsed : public EmbedderMojoTest, void OnLoadingFailed(const network::LoadingFailedParams& params) override { // The navigation should fail since HTTP requests are blackholed. EXPECT_EQ(params.GetErrorText(), "net::ERR_FILE_NOT_FOUND"); + DisableClientAndFinishAsynchronousTest(); + } + + void DisableClientAndFinishAsynchronousTest() { + devtools_client_->GetNetwork()->Disable(); + devtools_client_->GetNetwork()->RemoveObserver(this); FinishAsynchronousTest(); } }; diff --git a/chromium/headless/lib/headless_content_main_delegate.cc b/chromium/headless/lib/headless_content_main_delegate.cc index 193169ff8a0..4b7c1609035 100644 --- a/chromium/headless/lib/headless_content_main_delegate.cc +++ b/chromium/headless/lib/headless_content_main_delegate.cc @@ -5,6 +5,7 @@ #include "headless/lib/headless_content_main_delegate.h" #include "base/command_line.h" +#include "base/files/file_util.h" #include "base/path_service.h" #include "base/run_loop.h" #include "base/trace_event/trace_event.h" @@ -12,9 +13,9 @@ #include "content/public/common/content_switches.h" #include "headless/lib/browser/headless_browser_impl.h" #include "headless/lib/browser/headless_content_browser_client.h" -#include "headless/lib/renderer/headless_content_renderer_client.h" -#include "headless/lib/utility/headless_content_utility_client.h" #include "ui/base/resource/resource_bundle.h" +#include "ui/base/ui_base_switches.h" +#include "ui/gfx/switches.h" #include "ui/gl/gl_switches.h" #include "ui/ozone/public/ozone_switches.h" @@ -42,15 +43,21 @@ HeadlessContentMainDelegate::~HeadlessContentMainDelegate() { bool HeadlessContentMainDelegate::BasicStartupComplete(int* exit_code) { base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + // Make sure all processes know that we're in headless mode. + if (!command_line->HasSwitch(switches::kHeadless)) + command_line->AppendSwitch(switches::kHeadless); + if (browser_->options()->single_process_mode) command_line->AppendSwitch(switches::kSingleProcess); if (browser_->options()->disable_sandbox) command_line->AppendSwitch(switches::kNoSandbox); +#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. command_line->AppendSwitchASCII(switches::kOzonePlatform, "headless"); +#endif if (!browser_->options()->gl_implementation.empty()) { command_line->AppendSwitchASCII(switches::kUseGL, @@ -104,11 +111,24 @@ HeadlessContentMainDelegate* HeadlessContentMainDelegate::GetInstance() { // static void HeadlessContentMainDelegate::InitializeResourceBundle() { + base::FilePath dir_module; base::FilePath pak_file; - bool result = PathService::Get(base::DIR_MODULE, &pak_file); + bool result = PathService::Get(base::DIR_MODULE, &dir_module); DCHECK(result); - pak_file = pak_file.Append(FILE_PATH_LITERAL("headless_lib.pak")); - ui::ResourceBundle::InitSharedInstanceWithPakPath(pak_file); + + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + const std::string locale = command_line->GetSwitchValueASCII(switches::kLang); + ui::ResourceBundle::InitSharedInstanceWithLocale( + locale, nullptr, ui::ResourceBundle::DO_NOT_LOAD_COMMON_RESOURCES); + + // Try loading the headless library pak file first. If it doesn't exist (i.e., + // when we're running with the --headless switch), fall back to the browser's + // resource pak. + pak_file = dir_module.Append(FILE_PATH_LITERAL("headless_lib.pak")); + if (!base::PathExists(pak_file)) + pak_file = dir_module.Append(FILE_PATH_LITERAL("resources.pak")); + ResourceBundle::GetSharedInstance().AddDataPackFromPath( + pak_file, ui::SCALE_FACTOR_NONE); } content::ContentBrowserClient* @@ -117,16 +137,4 @@ HeadlessContentMainDelegate::CreateContentBrowserClient() { return browser_client_.get(); } -content::ContentRendererClient* -HeadlessContentMainDelegate::CreateContentRendererClient() { - renderer_client_.reset(new HeadlessContentRendererClient); - return renderer_client_.get(); -} - -content::ContentUtilityClient* -HeadlessContentMainDelegate::CreateContentUtilityClient() { - utility_client_.reset(new HeadlessContentUtilityClient); - return utility_client_.get(); -} - } // namespace headless diff --git a/chromium/headless/lib/headless_content_main_delegate.h b/chromium/headless/lib/headless_content_main_delegate.h index 00a20cf27dc..2e7aafe7973 100644 --- a/chromium/headless/lib/headless_content_main_delegate.h +++ b/chromium/headless/lib/headless_content_main_delegate.h @@ -10,18 +10,13 @@ #include "base/compiler_specific.h" #include "base/macros.h" #include "content/public/app/content_main_delegate.h" +#include "headless/lib/browser/headless_platform_event_source.h" #include "headless/lib/headless_content_client.h" -namespace content { -class BrowserContext; -} - namespace headless { class HeadlessBrowserImpl; class HeadlessContentBrowserClient; -class HeadlessContentUtilityClient; -class HeadlessContentRendererClient; class HeadlessContentMainDelegate : public content::ContentMainDelegate { public: @@ -37,8 +32,6 @@ class HeadlessContentMainDelegate : public content::ContentMainDelegate { const content::MainFunctionParams& main_function_params) override; void ZygoteForked() override; content::ContentBrowserClient* CreateContentBrowserClient() override; - content::ContentRendererClient* CreateContentRendererClient() override; - content::ContentUtilityClient* CreateContentUtilityClient() override; HeadlessBrowserImpl* browser() const { return browser_.get(); } @@ -50,9 +43,8 @@ class HeadlessContentMainDelegate : public content::ContentMainDelegate { static HeadlessContentMainDelegate* GetInstance(); std::unique_ptr<HeadlessContentBrowserClient> browser_client_; - std::unique_ptr<HeadlessContentRendererClient> renderer_client_; - std::unique_ptr<HeadlessContentUtilityClient> utility_client_; HeadlessContentClient content_client_; + HeadlessPlatformEventSource platform_event_source_; std::unique_ptr<HeadlessBrowserImpl> browser_; diff --git a/chromium/headless/lib/headless_devtools_client_browsertest.cc b/chromium/headless/lib/headless_devtools_client_browsertest.cc index 26252d8a7fd..6dbd79fad28 100644 --- a/chromium/headless/lib/headless_devtools_client_browsertest.cc +++ b/chromium/headless/lib/headless_devtools_client_browsertest.cc @@ -7,9 +7,12 @@ #include "base/json/json_reader.h" #include "content/public/browser/render_widget_host_view.h" #include "content/public/browser/web_contents.h" +#include "content/public/common/url_constants.h" #include "content/public/test/browser_test.h" #include "headless/lib/browser/headless_web_contents_impl.h" +#include "headless/public/devtools/domains/dom.h" #include "headless/public/devtools/domains/emulation.h" +#include "headless/public/devtools/domains/inspector.h" #include "headless/public/devtools/domains/network.h" #include "headless/public/devtools/domains/page.h" #include "headless/public/devtools/domains/runtime.h" @@ -62,6 +65,7 @@ class HeadlessDevToolsClientNavigationTest } void OnLoadEventFired(const page::LoadEventFiredParams& params) override { + devtools_client_->GetPage()->Disable(); devtools_client_->GetPage()->GetExperimental()->RemoveObserver(this); FinishAsynchronousTest(); } @@ -171,6 +175,7 @@ class HeadlessDevToolsClientObserverTest &content_type)); EXPECT_EQ("text/html", content_type); + devtools_client_->GetNetwork()->Disable(); devtools_client_->GetNetwork()->RemoveObserver(this); FinishAsynchronousTest(); } @@ -205,6 +210,9 @@ class HeadlessDevToolsClientExperimentalTest void OnFrameStoppedLoading( const page::FrameStoppedLoadingParams& params) override { + devtools_client_->GetPage()->Disable(); + devtools_client_->GetPage()->GetExperimental()->RemoveObserver(this); + // Check that a non-experimental command which has no return value can be // called with a void() callback. devtools_client_->GetPage()->Reload( @@ -660,4 +668,125 @@ class HeadlessDevToolsNavigationControlTest HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsNavigationControlTest); +class HeadlessCrashObserverTest : public HeadlessAsyncDevTooledBrowserTest, + inspector::ExperimentalObserver { + public: + void RunDevTooledTest() override { + devtools_client_->GetInspector()->GetExperimental()->AddObserver(this); + devtools_client_->GetInspector()->GetExperimental()->Enable( + headless::inspector::EnableParams::Builder().Build()); + devtools_client_->GetPage()->Enable(); + devtools_client_->GetPage()->Navigate(content::kChromeUICrashURL); + } + + void OnTargetCrashed(const inspector::TargetCrashedParams& params) override { + FinishAsynchronousTest(); + render_process_exited_ = true; + } + + // Make sure we don't fail because the renderer crashed! + void RenderProcessExited(base::TerminationStatus status, + int exit_code) override { + EXPECT_EQ(base::TERMINATION_STATUS_ABNORMAL_TERMINATION, status); + } +}; + +HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessCrashObserverTest); + +class HeadlessDevToolsClientAttachTest + : public HeadlessAsyncDevTooledBrowserTest { + public: + void RunDevTooledTest() override { + other_devtools_client_ = HeadlessDevToolsClient::Create(); + HeadlessDevToolsTarget* devtools_target = + web_contents_->GetDevToolsTarget(); + + // Try attaching: there's already a client attached. + EXPECT_FALSE(devtools_target->AttachClient(other_devtools_client_.get())); + EXPECT_TRUE(devtools_target->IsAttached()); + // Detach the existing client, attach the other client. + devtools_target->DetachClient(devtools_client_.get()); + EXPECT_FALSE(devtools_target->IsAttached()); + EXPECT_TRUE(devtools_target->AttachClient(other_devtools_client_.get())); + EXPECT_TRUE(devtools_target->IsAttached()); + + // Now, let's make sure this devtools client works. + other_devtools_client_->GetRuntime()->Evaluate( + "24 * 7", base::Bind(&HeadlessDevToolsClientAttachTest::OnFirstResult, + base::Unretained(this))); + } + + 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); + + HeadlessDevToolsTarget* devtools_target = + web_contents_->GetDevToolsTarget(); + + // Try attach, then force-attach the original client. + EXPECT_FALSE(devtools_target->AttachClient(devtools_client_.get())); + devtools_target->ForceAttachClient(devtools_client_.get()); + EXPECT_TRUE(devtools_target->IsAttached()); + + devtools_client_->GetRuntime()->Evaluate( + "27 * 4", base::Bind(&HeadlessDevToolsClientAttachTest::OnSecondResult, + base::Unretained(this))); + } + + 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); + + // If everything worked, this call will not crash, since it + // detaches devtools_client_. + FinishAsynchronousTest(); + } + + protected: + std::unique_ptr<HeadlessDevToolsClient> other_devtools_client_; +}; + +HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsClientAttachTest); + +class HeadlessDevToolsMethodCallErrorTest + : public HeadlessAsyncDevTooledBrowserTest, + public page::Observer { + public: + void RunDevTooledTest() override { + EXPECT_TRUE(embedded_test_server()->Start()); + devtools_client_->GetPage()->AddObserver(this); + devtools_client_->GetPage()->Enable(); + devtools_client_->GetPage()->Navigate( + embedded_test_server()->GetURL("/hello.html").spec()); + } + + void OnLoadEventFired(const page::LoadEventFiredParams& params) override { + devtools_client_->GetPage()->GetExperimental()->RemoveObserver(this); + devtools_client_->GetDOM()->GetDocument( + base::Bind(&HeadlessDevToolsMethodCallErrorTest::OnGetDocument, + base::Unretained(this))); + } + + void OnGetDocument(std::unique_ptr<dom::GetDocumentResult> result) { + devtools_client_->GetDOM()->QuerySelector( + dom::QuerySelectorParams::Builder() + .SetNodeId(result->GetRoot()->GetNodeId()) + .SetSelector("<o_O>") + .Build(), + base::Bind(&HeadlessDevToolsMethodCallErrorTest::OnQuerySelector, + base::Unretained(this))); + } + + void OnQuerySelector(std::unique_ptr<dom::QuerySelectorResult> result) { + EXPECT_EQ(nullptr, result); + FinishAsynchronousTest(); + } +}; + +HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsMethodCallErrorTest); + } // namespace headless diff --git a/chromium/headless/lib/headless_web_contents_browsertest.cc b/chromium/headless/lib/headless_web_contents_browsertest.cc index 51f519c9055..693e2dceeaa 100644 --- a/chromium/headless/lib/headless_web_contents_browsertest.cc +++ b/chromium/headless/lib/headless_web_contents_browsertest.cc @@ -6,8 +6,10 @@ #include <string> #include <vector> +#include "base/base64.h" #include "content/public/test/browser_test.h" #include "headless/public/devtools/domains/page.h" +#include "headless/public/devtools/domains/runtime.h" #include "headless/public/devtools/domains/security.h" #include "headless/public/headless_browser.h" #include "headless/public/headless_devtools_client.h" @@ -15,6 +17,9 @@ #include "headless/test/headless_browser_test.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "third_party/skia/include/core/SkColor.h" +#include "ui/gfx/codec/png_codec.h" #include "ui/gfx/geometry/size.h" #include "url/gurl.h" @@ -56,10 +61,41 @@ IN_PROC_BROWSER_TEST_F(HeadlessWebContentsTest, WindowOpen) { browser_context->GetAllWebContents().size()); } +namespace { +bool DecodePNG(std::string base64_data, SkBitmap* bitmap) { + std::string png_data; + if (!base::Base64Decode(base64_data, &png_data)) + return false; + return gfx::PNGCodec::Decode( + reinterpret_cast<unsigned const char*>(png_data.data()), png_data.size(), + bitmap); +} +} // namespace + +// Parameter specifies whether --disable-gpu should be used. class HeadlessWebContentsScreenshotTest - : public HeadlessAsyncDevTooledBrowserTest { + : public HeadlessAsyncDevTooledBrowserTest, + public ::testing::WithParamInterface<bool> { public: + void SetUp() override { + EnablePixelOutput(); + if (GetParam()) + UseSoftwareCompositing(); + HeadlessAsyncDevTooledBrowserTest::SetUp(); + } + void RunDevTooledTest() override { + std::unique_ptr<runtime::EvaluateParams> params = + runtime::EvaluateParams::Builder() + .SetExpression("document.body.style.background = '#0000ff'") + .Build(); + devtools_client_->GetRuntime()->Evaluate( + std::move(params), + base::Bind(&HeadlessWebContentsScreenshotTest::OnPageSetupCompleted, + base::Unretained(this))); + } + + void OnPageSetupCompleted(std::unique_ptr<runtime::EvaluateResult> result) { devtools_client_->GetPage()->GetExperimental()->CaptureScreenshot( page::CaptureScreenshotParams::Builder().Build(), base::Bind(&HeadlessWebContentsScreenshotTest::OnScreenshotCaptured, @@ -68,12 +104,26 @@ class HeadlessWebContentsScreenshotTest void OnScreenshotCaptured( std::unique_ptr<page::CaptureScreenshotResult> result) { - EXPECT_LT(0U, result->GetData().length()); + std::string base64 = result->GetData(); + EXPECT_LT(0U, base64.length()); + SkBitmap result_bitmap; + EXPECT_TRUE(DecodePNG(base64, &result_bitmap)); + + EXPECT_EQ(800, result_bitmap.width()); + EXPECT_EQ(600, result_bitmap.height()); + SkColor actual_color = result_bitmap.getColor(400, 300); + SkColor expected_color = SkColorSetRGB(0x00, 0x00, 0xff); + EXPECT_EQ(expected_color, actual_color); FinishAsynchronousTest(); } }; -HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessWebContentsScreenshotTest); +HEADLESS_ASYNC_DEVTOOLED_TEST_P(HeadlessWebContentsScreenshotTest); + +// Instantiate test case for both software and gpu compositing modes. +INSTANTIATE_TEST_CASE_P(HeadlessWebContentsScreenshotTests, + HeadlessWebContentsScreenshotTest, + ::testing::Bool()); class HeadlessWebContentsSecurityTest : public HeadlessAsyncDevTooledBrowserTest, @@ -88,8 +138,10 @@ class HeadlessWebContentsSecurityTest void OnSecurityStateChanged( const security::SecurityStateChangedParams& params) override { EXPECT_EQ(security::SecurityState::NEUTRAL, params.GetSecurityState()); - EXPECT_TRUE(params.HasExplanations()); + devtools_client_->GetSecurity()->GetExperimental()->Disable( + security::DisableParams::Builder().Build()); + devtools_client_->GetSecurity()->GetExperimental()->RemoveObserver(this); FinishAsynchronousTest(); } }; diff --git a/chromium/headless/lib/renderer/headless_content_renderer_client.cc b/chromium/headless/lib/renderer/headless_content_renderer_client.cc deleted file mode 100644 index 82faf2165a5..00000000000 --- a/chromium/headless/lib/renderer/headless_content_renderer_client.cc +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2015 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/lib/renderer/headless_content_renderer_client.h" - -#include "base/strings/utf_string_conversions.h" -#include "content/public/renderer/render_frame.h" - -namespace headless { - -HeadlessContentRendererClient::HeadlessContentRendererClient() {} - -HeadlessContentRendererClient::~HeadlessContentRendererClient() {} - -} // namespace headless diff --git a/chromium/headless/lib/renderer/headless_content_renderer_client.h b/chromium/headless/lib/renderer/headless_content_renderer_client.h deleted file mode 100644 index a10ff81752b..00000000000 --- a/chromium/headless/lib/renderer/headless_content_renderer_client.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2015 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_LIB_RENDERER_HEADLESS_CONTENT_RENDERER_CLIENT_H_ -#define HEADLESS_LIB_RENDERER_HEADLESS_CONTENT_RENDERER_CLIENT_H_ - -#include "content/public/renderer/content_renderer_client.h" - -namespace headless { - -class HeadlessContentRendererClient : public content::ContentRendererClient { - public: - HeadlessContentRendererClient(); - ~HeadlessContentRendererClient() override; - - DISALLOW_COPY_AND_ASSIGN(HeadlessContentRendererClient); -}; - -} // namespace headless - -#endif // HEADLESS_LIB_RENDERER_HEADLESS_CONTENT_RENDERER_CLIENT_H_ diff --git a/chromium/headless/lib/utility/headless_content_utility_client.cc b/chromium/headless/lib/utility/headless_content_utility_client.cc deleted file mode 100644 index 952e7485ab6..00000000000 --- a/chromium/headless/lib/utility/headless_content_utility_client.cc +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2015 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/lib/utility/headless_content_utility_client.h" - -namespace headless { - -HeadlessContentUtilityClient::HeadlessContentUtilityClient() {} - -HeadlessContentUtilityClient::~HeadlessContentUtilityClient() {} - -} // namespace headless diff --git a/chromium/headless/lib/utility/headless_content_utility_client.h b/chromium/headless/lib/utility/headless_content_utility_client.h deleted file mode 100644 index 0a522f2c54b..00000000000 --- a/chromium/headless/lib/utility/headless_content_utility_client.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2015 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_LIB_UTILITY_HEADLESS_CONTENT_UTILITY_CLIENT_H_ -#define HEADLESS_LIB_UTILITY_HEADLESS_CONTENT_UTILITY_CLIENT_H_ - -#include "content/public/utility/content_utility_client.h" - -namespace headless { - -class HeadlessContentUtilityClient : public content::ContentUtilityClient { - public: - HeadlessContentUtilityClient(); - ~HeadlessContentUtilityClient() override; - - DISALLOW_COPY_AND_ASSIGN(HeadlessContentUtilityClient); -}; - -} // namespace headless - -#endif // HEADLESS_LIB_UTILITY_HEADLESS_CONTENT_UTILITY_CLIENT_H_ diff --git a/chromium/headless/public/domains/types_unittest.cc b/chromium/headless/public/domains/types_unittest.cc index e5c0efa7e5a..db07df1a58d 100644 --- a/chromium/headless/public/domains/types_unittest.cc +++ b/chromium/headless/public/domains/types_unittest.cc @@ -3,7 +3,9 @@ // found in the LICENSE file. #include "base/json/json_reader.h" +#include "base/json/json_string_value_serializer.h" #include "headless/public/devtools/domains/accessibility.h" +#include "headless/public/devtools/domains/dom.h" #include "headless/public/devtools/domains/memory.h" #include "headless/public/devtools/domains/page.h" #include "testing/gtest/include/gtest/gtest.h" @@ -202,15 +204,40 @@ TEST(TypesTest, AnyProperty) { .SetValue(std::move(value)) .Build()); EXPECT_TRUE(object); - EXPECT_EQ(base::Value::TYPE_INTEGER, object->GetValue()->GetType()); + EXPECT_EQ(base::Value::Type::INTEGER, object->GetValue()->GetType()); std::unique_ptr<accessibility::AXValue> clone(object->Clone()); EXPECT_TRUE(clone); - EXPECT_EQ(base::Value::TYPE_INTEGER, clone->GetValue()->GetType()); + EXPECT_EQ(base::Value::Type::INTEGER, clone->GetValue()->GetType()); int clone_value; EXPECT_TRUE(clone->GetValue()->GetAsInteger(&clone_value)); EXPECT_EQ(123, clone_value); } +TEST(TypesTest, ComplexObjectClone) { + std::vector<std::unique_ptr<dom::Node>> child_nodes; + child_nodes.emplace_back(dom::Node::Builder() + .SetNodeId(1) + .SetBackendNodeId(2) + .SetNodeType(3) + .SetNodeName("-blink-blink") + .SetLocalName("-blink-blink") + .SetNodeValue("-blink-blink") + .Build()); + std::unique_ptr<dom::SetChildNodesParams> params = + dom::SetChildNodesParams::Builder() + .SetParentId(123) + .SetNodes(std::move(child_nodes)) + .Build(); + std::unique_ptr<dom::SetChildNodesParams> clone = params->Clone(); + ASSERT_NE(nullptr, clone); + + std::string orig; + JSONStringValueSerializer(&orig).Serialize(*params->Serialize()); + std::string clone_value; + JSONStringValueSerializer(&clone_value).Serialize(*clone->Serialize()); + EXPECT_EQ(orig, clone_value); +} + } // namespace headless diff --git a/chromium/headless/public/headless_browser.h b/chromium/headless/public/headless_browser.h index 021e91064fb..c3043f006ad 100644 --- a/chromium/headless/public/headless_browser.h +++ b/chromium/headless/public/headless_browser.h @@ -56,6 +56,12 @@ class HEADLESS_EXPORT HeadlessBrowser { virtual HeadlessBrowserContext* GetBrowserContextForId( const std::string& id) = 0; + // Allows setting and getting the browser context that DevTools will create + // new targets in by default. + virtual void SetDefaultBrowserContext( + HeadlessBrowserContext* browser_context) = 0; + virtual HeadlessBrowserContext* GetDefaultBrowserContext() = 0; + // Returns a task runner for submitting work to the browser file thread. virtual scoped_refptr<base::SingleThreadTaskRunner> BrowserFileThread() const = 0; diff --git a/chromium/headless/public/headless_devtools_target.h b/chromium/headless/public/headless_devtools_target.h index 941fe6f51e2..e8047f39b8f 100644 --- a/chromium/headless/public/headless_devtools_target.h +++ b/chromium/headless/public/headless_devtools_target.h @@ -20,14 +20,21 @@ class HEADLESS_EXPORT HeadlessDevToolsTarget { // Attach or detach a client to this target. A client must be attached in // order to send commands or receive notifications from the target. // - // A single client may be attached to at most one target at a time. Note that - // currently also only one client may be attached to a single target at a - // time. + // A single client may be attached to at most one target at a time. If + // the target already has a client attached, AttachClient will return false. + // ForceAttachClient will detach any existing connection before attaching + // |client|. + // Note that currently also only one client may be attached to a single target + // at a time. // // |client| must outlive this target. - virtual void AttachClient(HeadlessDevToolsClient* client) = 0; + virtual bool AttachClient(HeadlessDevToolsClient* client) = 0; + virtual void ForceAttachClient(HeadlessDevToolsClient* client) = 0; virtual void DetachClient(HeadlessDevToolsClient* client) = 0; + // Returns true if a devtools client is attached. + virtual bool IsAttached() = 0; + private: DISALLOW_COPY_AND_ASSIGN(HeadlessDevToolsTarget); }; diff --git a/chromium/headless/public/headless_web_contents.h b/chromium/headless/public/headless_web_contents.h index 0ca6a588bb5..74dc4df9049 100644 --- a/chromium/headless/public/headless_web_contents.h +++ b/chromium/headless/public/headless_web_contents.h @@ -11,6 +11,7 @@ #include "base/callback.h" #include "base/macros.h" +#include "base/process/kill.h" #include "headless/public/headless_export.h" #include "mojo/public/cpp/bindings/interface_request.h" #include "ui/gfx/geometry/size.h" @@ -39,6 +40,16 @@ class HEADLESS_EXPORT HeadlessWebContents { // TODO(altimin): Support this event for pages that aren't created by us. virtual void DevToolsTargetReady() {} + // This method is invoked when the process of the observed RenderProcessHost + // exits (either normally or with a crash). To determine if the process + // closed normally or crashed, examine the |status| parameter. + // + // If |status| is TERMINATION_STATUS_LAUNCH_FAILED then |exit_code| will + // contain a platform specific launch failure error code. Otherwise, it will + // contain the exit code for the process. + virtual void RenderProcessExited(base::TerminationStatus status, + int exit_code) {} + protected: Observer() {} virtual ~Observer() {} diff --git a/chromium/headless/public/internal/value_conversions.h b/chromium/headless/public/internal/value_conversions.h index 4a85a02a464..78ac41fc4cb 100644 --- a/chromium/headless/public/internal/value_conversions.h +++ b/chromium/headless/public/internal/value_conversions.h @@ -67,7 +67,7 @@ std::unique_ptr<base::Value> ToValueImpl(const std::vector<T>& vector, template <typename T> std::unique_ptr<base::Value> ToValueImpl(const std::unique_ptr<T>& value, std::unique_ptr<T>*) { - return ToValue(value.get()); + return ToValue(*value); } // FromValue specializations for basic types. diff --git a/chromium/headless/public/util/deterministic_http_protocol_handler.h b/chromium/headless/public/util/deterministic_http_protocol_handler.h index da0a70d1e4b..89d76cbc692 100644 --- a/chromium/headless/public/util/deterministic_http_protocol_handler.h +++ b/chromium/headless/public/util/deterministic_http_protocol_handler.h @@ -17,7 +17,6 @@ class URLRequestJobFactory; namespace headless { class DeterministicDispatcher; -class HeadlessBrowserContext; // A deterministic protocol handler. Requests made to this protocol handler // will return in order of creation, regardless of what order the network diff --git a/chromium/headless/public/util/dom_tree_extractor_browsertest.cc b/chromium/headless/public/util/dom_tree_extractor_browsertest.cc index 57b6a54869f..22b237960c5 100644 --- a/chromium/headless/public/util/dom_tree_extractor_browsertest.cc +++ b/chromium/headless/public/util/dom_tree_extractor_browsertest.cc @@ -49,13 +49,14 @@ class DomTreeExtractorBrowserTest : public HeadlessAsyncDevTooledBrowserTest, } void OnLoadEventFired(const page::LoadEventFiredParams& params) override { + devtools_client_->GetPage()->Disable(); devtools_client_->GetPage()->RemoveObserver(this); extractor_.reset(new DomTreeExtractor(devtools_client_.get())); std::vector<std::string> css_whitelist = { - "color", "display", "font-style", "margin-left", - "margin-right", "margin-top", "margin-bottom"}; + "color", "display", "font-style", "font-family", + "margin-left", "margin-right", "margin-top", "margin-bottom"}; extractor_->ExtractDomTree( css_whitelist, base::Bind(&DomTreeExtractorBrowserTest::OnDomTreeExtracted, @@ -181,7 +182,7 @@ class DomTreeExtractorBrowserTest : public HeadlessAsyncDevTooledBrowserTest, "x": 0.0, "y": 0.0 }, - "childIndices": [ 2, 5 ], + "childIndices": [ 2, 6 ], "frameId": "?", "localName": "html", "nodeId": 2, @@ -194,7 +195,7 @@ class DomTreeExtractorBrowserTest : public HeadlessAsyncDevTooledBrowserTest, R"raw_string({ "attributes": [ ], "backendNodeId": 5, - "childIndices": [ 3 ], + "childIndices": [ 3, 5 ], "localName": "head", "nodeId": 3, "nodeName": "HEAD", @@ -223,17 +224,29 @@ class DomTreeExtractorBrowserTest : public HeadlessAsyncDevTooledBrowserTest, })raw_string", R"raw_string({ - "attributes": [ ], + "attributes": [ "href", "dom_tree_test.css", "rel", "stylesheet", + "type", "text/css" ], "backendNodeId": 8, + "childIndices": [ ], + "localName": "link", + "nodeId": 6, + "nodeName": "LINK", + "nodeType": 1, + "nodeValue": "" + })raw_string", + + R"raw_string({ + "attributes": [ ], + "backendNodeId": 9, "boundingBox": { "height": 584.0, "width": 784.0, "x": 8.0, "y": 8.0 }, - "childIndices": [ 6 ], + "childIndices": [ 7 ], "localName": "body", - "nodeId": 6, + "nodeId": 7, "nodeName": "BODY", "nodeType": 1, "nodeValue": "", @@ -242,16 +255,16 @@ class DomTreeExtractorBrowserTest : public HeadlessAsyncDevTooledBrowserTest, R"raw_string({ "attributes": [ "id", "id1" ], - "backendNodeId": 9, + "backendNodeId": 10, "boundingBox": { - "height": 367.0, + "height": 354.0, "width": 784.0, "x": 8.0, "y": 8.0 }, - "childIndices": [ 7, 9, 16 ], + "childIndices": [ 8, 10, 17 ], "localName": "div", - "nodeId": 7, + "nodeId": 8, "nodeName": "DIV", "nodeType": 1, "nodeValue": "", @@ -259,17 +272,17 @@ class DomTreeExtractorBrowserTest : public HeadlessAsyncDevTooledBrowserTest, })raw_string", R"raw_string({ - "attributes": [ "style", "color: red" ], - "backendNodeId": 10, + "attributes": [ "class", "red" ], + "backendNodeId": 11, "boundingBox": { - "height": 37.0, + "height": 32.0, "width": 784.0, "x": 8.0, "y": 8.0 }, - "childIndices": [ 8 ], + "childIndices": [ 9 ], "localName": "h1", - "nodeId": 8, + "nodeId": 9, "nodeName": "H1", "nodeType": 1, "nodeValue": "", @@ -277,17 +290,17 @@ class DomTreeExtractorBrowserTest : public HeadlessAsyncDevTooledBrowserTest, })raw_string", R"raw_string({ - "backendNodeId": 11, + "backendNodeId": 12, "boundingBox": { - "height": 36.0, - "width": 143.0, + "height": 32.0, + "width": 320.0, "x": 8.0, "y": 8.0 }, "inlineTextNodes": [ { "boundingBox": { - "height": 36.0, - "width": 142.171875, + "height": 32.0, + "width": 320.0, "x": 8.0, "y": 8.0 }, @@ -296,7 +309,7 @@ class DomTreeExtractorBrowserTest : public HeadlessAsyncDevTooledBrowserTest, } ], "layoutText": "Some text.", "localName": "", - "nodeId": 9, + "nodeId": 10, "nodeName": "#text", "nodeType": 3, "nodeValue": "Some text.", @@ -304,33 +317,33 @@ class DomTreeExtractorBrowserTest : public HeadlessAsyncDevTooledBrowserTest, })raw_string", R"raw_string({ - "attributes": [ - "src", "/iframe.html", "width", "400", "height", "200" ], - "backendNodeId": 12, + "attributes": [ "src", "/iframe.html", "width", "400", "height", + "200" ], + "backendNodeId": 13, "boundingBox": { "height": 205.0, "width": 404.0, "x": 8.0, - "y": 66.0 + "y": 61.0 }, "childIndices": [ ], - "contentDocumentIndex": 10, + "contentDocumentIndex": 11, "frameId": "?", "localName": "iframe", - "nodeId": 10, + "nodeId": 11, "nodeName": "IFRAME", "nodeType": 1, "nodeValue": "", - "styleIndex": 4 + "styleIndex": 6 })raw_string", R"raw_string({ - "backendNodeId": 13, + "backendNodeId": 14, "baseURL": "http://127.0.0.1/iframe.html", - "childIndices": [ 11 ], + "childIndices": [ 12 ], "documentURL": "http://127.0.0.1/iframe.html", "localName": "", - "nodeId": 11, + "nodeId": 12, "nodeName": "#document", "nodeType": 9, "nodeValue": "", @@ -339,29 +352,29 @@ class DomTreeExtractorBrowserTest : public HeadlessAsyncDevTooledBrowserTest, R"raw_string({ "attributes": [ ], - "backendNodeId": 14, + "backendNodeId": 15, "boundingBox": { "height": 200.0, "width": 400.0, "x": 10.0, - "y": 68.0 + "y": 63.0 }, - "childIndices": [ 12, 13 ], + "childIndices": [ 13, 14 ], "frameId": "?", "localName": "html", - "nodeId": 12, + "nodeId": 13, "nodeName": "HTML", "nodeType": 1, "nodeValue": "", - "styleIndex": 0 + "styleIndex": 3 })raw_string", R"raw_string({ "attributes": [ ], - "backendNodeId": 15, + "backendNodeId": 16, "childIndices": [ ], "localName": "head", - "nodeId": 13, + "nodeId": 14, "nodeName": "HEAD", "nodeType": 1, "nodeValue": "" @@ -369,42 +382,42 @@ class DomTreeExtractorBrowserTest : public HeadlessAsyncDevTooledBrowserTest, R"raw_string({ "attributes": [ ], - "backendNodeId": 16, + "backendNodeId": 17, "boundingBox": { "height": 171.0, "width": 384.0, "x": 18.0, - "y": 76.0 + "y": 71.0 }, - "childIndices": [ 14 ], + "childIndices": [ 15 ], "localName": "body", - "nodeId": 14, + "nodeId": 15, "nodeName": "BODY", "nodeType": 1, "nodeValue": "", - "styleIndex": 1 + "styleIndex": 4 })raw_string", R"raw_string({ "attributes": [ ], - "backendNodeId": 17, + "backendNodeId": 18, "boundingBox": { "height": 37.0, "width": 384.0, "x": 18.0, - "y": 76.0 + "y": 71.0 }, - "childIndices": [ 15 ], + "childIndices": [ 16 ], "localName": "h1", - "nodeId": 15, + "nodeId": 16, "nodeName": "H1", "nodeType": 1, "nodeValue": "", - "styleIndex": 3 + "styleIndex": 5 })raw_string", R"raw_string({ - "backendNodeId": 18, + "backendNodeId": 19, "boundingBox": { "height": 36.0, "width": 308.0, @@ -423,25 +436,25 @@ class DomTreeExtractorBrowserTest : public HeadlessAsyncDevTooledBrowserTest, } ], "layoutText": "Hello from the iframe!", "localName": "", - "nodeId": 16, + "nodeId": 17, "nodeName": "#text", "nodeType": 3, "nodeValue": "Hello from the iframe!", - "styleIndex": 3 + "styleIndex": 5 })raw_string", R"raw_string({ "attributes": [ "id", "id2" ], - "backendNodeId": 19, + "backendNodeId": 20, "boundingBox": { - "height": 105.0, + "height": 97.0, "width": 784.0, "x": 8.0, - "y": 270.0 + "y": 265.0 }, - "childIndices": [ 17 ], + "childIndices": [ 18 ], "localName": "div", - "nodeId": 17, + "nodeId": 18, "nodeName": "DIV", "nodeType": 1, "nodeValue": "", @@ -450,16 +463,16 @@ class DomTreeExtractorBrowserTest : public HeadlessAsyncDevTooledBrowserTest, R"raw_string({ "attributes": [ "id", "id3" ], - "backendNodeId": 20, + "backendNodeId": 21, "boundingBox": { - "height": 105.0, + "height": 97.0, "width": 784.0, "x": 8.0, - "y": 270.0 + "y": 265.0 }, - "childIndices": [ 18 ], + "childIndices": [ 19 ], "localName": "div", - "nodeId": 18, + "nodeId": 19, "nodeName": "DIV", "nodeType": 1, "nodeValue": "", @@ -468,16 +481,16 @@ class DomTreeExtractorBrowserTest : public HeadlessAsyncDevTooledBrowserTest, R"raw_string({ "attributes": [ "id", "id4" ], - "backendNodeId": 21, + "backendNodeId": 22, "boundingBox": { - "height": 105.0, + "height": 97.0, "width": 784.0, "x": 8.0, - "y": 270.0 + "y": 265.0 }, - "childIndices": [ 19, 21, 23, 24 ], + "childIndices": [ 20, 22, 24, 25 ], "localName": "div", - "nodeId": 19, + "nodeId": 20, "nodeName": "DIV", "nodeType": 1, "nodeValue": "", @@ -486,97 +499,97 @@ class DomTreeExtractorBrowserTest : public HeadlessAsyncDevTooledBrowserTest, R"raw_string({ "attributes": [ "href", "https://www.google.com" ], - "backendNodeId": 22, + "backendNodeId": 23, "boundingBox": { - "height": 18.0, - "width": 53.0, + "height": 17.0, + "width": 112.0, "x": 8.0, - "y": 270.0 + "y": 265.0 }, - "childIndices": [ 20 ], + "childIndices": [ 21 ], "localName": "a", - "nodeId": 20, + "nodeId": 21, "nodeName": "A", "nodeType": 1, "nodeValue": "", - "styleIndex": 5 + "styleIndex": 7 })raw_string", R"raw_string({ - "backendNodeId": 23, + "backendNodeId": 24, "boundingBox": { - "height": 18.0, - "width": 53.0, + "height": 17.0, + "width": 112.0, "x": 8.0, - "y": 270.0 + "y": 265.0 }, "inlineTextNodes": [ { "boundingBox": { - "height": 17.0, - "width": 52.421875, + "height": 16.0, + "width": 112.0, "x": 8.0, - "y": 270.4375 + "y": 265.4375 }, "numCharacters": 7, "startCharacterIndex": 0 } ], "layoutText": "Google!", "localName": "", - "nodeId": 21, + "nodeId": 22, "nodeName": "#text", "nodeType": 3, "nodeValue": "Google!", - "styleIndex": 5 + "styleIndex": 7 })raw_string", R"raw_string({ "attributes": [ ], - "backendNodeId": 24, + "backendNodeId": 25, "boundingBox": { - "height": 19.0, + "height": 17.0, "width": 784.0, "x": 8.0, - "y": 304.0 + "y": 297.0 }, - "childIndices": [ 22 ], + "childIndices": [ 23 ], "localName": "p", - "nodeId": 22, + "nodeId": 23, "nodeName": "P", "nodeType": 1, "nodeValue": "", - "styleIndex": 6 + "styleIndex": 8 })raw_string", R"raw_string({ - "backendNodeId": 25, + "backendNodeId": 26, "boundingBox": { - "height": 18.0, - "width": 85.0, + "height": 17.0, + "width": 192.0, "x": 8.0, - "y": 304.0 + "y": 297.0 }, "inlineTextNodes": [ { "boundingBox": { - "height": 17.0, - "width": 84.84375, + "height": 16.0, + "width": 192.0, "x": 8.0, - "y": 304.4375 + "y": 297.4375 }, "numCharacters": 12, "startCharacterIndex": 0 } ], "layoutText": "A paragraph!", "localName": "", - "nodeId": 23, + "nodeId": 24, "nodeName": "#text", "nodeType": 3, "nodeValue": "A paragraph!", - "styleIndex": 6 + "styleIndex": 8 })raw_string", R"raw_string({ "attributes": [ ], - "backendNodeId": 26, + "backendNodeId": 27, "boundingBox": { "height": 0.0, "width": 0.0, @@ -586,138 +599,139 @@ class DomTreeExtractorBrowserTest : public HeadlessAsyncDevTooledBrowserTest, "childIndices": [ ], "inlineTextNodes": [ { "boundingBox": { - "height": 17.0, + "height": 16.0, "width": 0.0, "x": 8.0, - "y": 338.4375 + "y": 329.4375 }, "numCharacters": 1, "startCharacterIndex": 0 } ], "layoutText": "\n", "localName": "br", - "nodeId": 24, + "nodeId": 25, "nodeName": "BR", "nodeType": 1, "nodeValue": "", - "styleIndex": 4 + "styleIndex": 6 })raw_string", R"raw_string({ - "attributes": [ "style", "color: green" ], - "backendNodeId": 27, + "attributes": [ "class", "green" ], + "backendNodeId": 28, "boundingBox": { - "height": 19.0, + "height": 17.0, "width": 784.0, "x": 8.0, - "y": 356.0 + "y": 345.0 }, - "childIndices": [ 25, 26, 28 ], + "childIndices": [ 26, 27, 29 ], "localName": "div", - "nodeId": 25, + "nodeId": 26, "nodeName": "DIV", "nodeType": 1, "nodeValue": "", - "styleIndex": 7 - })raw_string", + "styleIndex": 9 + } + )raw_string", R"raw_string({ - "backendNodeId": 28, + "backendNodeId": 29, "boundingBox": { - "height": 18.0, - "width": 41.0, + "height": 17.0, + "width": 80.0, "x": 8.0, - "y": 356.0 + "y": 345.0 }, "inlineTextNodes": [ { "boundingBox": { - "height": 17.0, - "width": 40.4375, + "height": 16.0, + "width": 80.0, "x": 8.0, - "y": 356.4375 + "y": 345.4375 }, "numCharacters": 5, "startCharacterIndex": 0 } ], "layoutText": "Some ", "localName": "", - "nodeId": 26, + "nodeId": 27, "nodeName": "#text", "nodeType": 3, "nodeValue": "Some ", - "styleIndex": 7 + "styleIndex": 9 })raw_string", R"raw_string({ "attributes": [ ], - "backendNodeId": 29, + "backendNodeId": 30, "boundingBox": { - "height": 18.0, - "width": 37.0, - "x": 48.0, - "y": 356.0 + "height": 17.0, + "width": 80.0, + "x": 88.0, + "y": 345.0 }, - "childIndices": [ 27 ], + "childIndices": [ 28 ], "localName": "em", - "nodeId": 27, + "nodeId": 28, "nodeName": "EM", "nodeType": 1, "nodeValue": "", - "styleIndex": 8 + "styleIndex": 10 })raw_string", R"raw_string({ - "backendNodeId": 30, + "backendNodeId": 31, "boundingBox": { - "height": 18.0, - "width": 37.0, - "x": 48.0, - "y": 356.0 + "height": 17.0, + "width": 80.0, + "x": 88.0, + "y": 345.0 }, "inlineTextNodes": [ { "boundingBox": { - "height": 17.0, - "width": 35.828125, - "x": 48.4375, - "y": 356.4375 + "height": 16.0, + "width": 80.0, + "x": 88.0, + "y": 345.4375 }, "numCharacters": 5, "startCharacterIndex": 0 } ], "layoutText": "green", "localName": "", - "nodeId": 28, + "nodeId": 29, "nodeName": "#text", "nodeType": 3, "nodeValue": "green", - "styleIndex": 8 + "styleIndex": 10 })raw_string", R"raw_string({ - "backendNodeId": 31, + "backendNodeId": 32, "boundingBox": { - "height": 18.0, - "width": 41.0, - "x": 84.0, - "y": 356.0 + "height": 17.0, + "width": 128.0, + "x": 168.0, + "y": 345.0 }, "inlineTextNodes": [ { "boundingBox": { - "height": 17.0, - "width": 39.984375, - "x": 84.265625, - "y": 356.4375 + "height": 16.0, + "width": 128.0, + "x": 168.0, + "y": 345.4375 }, "numCharacters": 8, "startCharacterIndex": 0 } ], "layoutText": " text...", "localName": "", - "nodeId": 29, + "nodeId": 30, "nodeName": "#text", "nodeType": 3, "nodeValue": " text...", - "styleIndex": 7 + "styleIndex": 9 })raw_string"}; EXPECT_EQ(expected_dom_nodes.size(), dom_nodes.size()); @@ -736,6 +750,7 @@ class DomTreeExtractorBrowserTest : public HeadlessAsyncDevTooledBrowserTest, R"raw_string({ "color": "rgb(0, 0, 0)", "display": "block", + "font-family": "ahem", "font-style": "normal", "margin-bottom": "0px", "margin-left": "0px", @@ -746,6 +761,7 @@ class DomTreeExtractorBrowserTest : public HeadlessAsyncDevTooledBrowserTest, R"raw_string({ "color": "rgb(0, 0, 0)", "display": "block", + "font-family": "ahem", "font-style": "normal", "margin-bottom": "8px", "margin-left": "8px", @@ -756,6 +772,7 @@ class DomTreeExtractorBrowserTest : public HeadlessAsyncDevTooledBrowserTest, R"raw_string({ "color": "rgb(255, 0, 0)", "display": "block", + "font-family": "ahem", "font-style": "normal", "margin-bottom": "21.44px", "margin-left": "0px", @@ -766,6 +783,29 @@ class DomTreeExtractorBrowserTest : public HeadlessAsyncDevTooledBrowserTest, R"raw_string({ "color": "rgb(0, 0, 0)", "display": "block", + "font-family": "\"Times New Roman\"", + "font-style": "normal", + "margin-bottom": "0px", + "margin-left": "0px", + "margin-right": "0px", + "margin-top": "0px" + })raw_string", + + R"raw_string({ + "color": "rgb(0, 0, 0)", + "display": "block", + "font-family": "\"Times New Roman\"", + "font-style": "normal", + "margin-bottom": "8px", + "margin-left": "8px", + "margin-right": "8px", + "margin-top": "8px" + })raw_string", + + R"raw_string({ + "color": "rgb(0, 0, 0)", + "display": "block", + "font-family": "\"Times New Roman\"", "font-style": "normal", "margin-bottom": "21.44px", "margin-left": "0px", @@ -776,6 +816,7 @@ class DomTreeExtractorBrowserTest : public HeadlessAsyncDevTooledBrowserTest, R"raw_string({ "color": "rgb(0, 0, 0)", "display": "inline", + "font-family": "ahem", "font-style": "normal", "margin-bottom": "0px", "margin-left": "0px", @@ -786,6 +827,7 @@ class DomTreeExtractorBrowserTest : public HeadlessAsyncDevTooledBrowserTest, R"raw_string({ "color": "rgb(0, 0, 238)", "display": "inline", + "font-family": "ahem", "font-style": "normal", "margin-bottom": "0px", "margin-left": "0px", @@ -796,6 +838,7 @@ class DomTreeExtractorBrowserTest : public HeadlessAsyncDevTooledBrowserTest, R"raw_string({ "color": "rgb(0, 0, 0)", "display": "block", + "font-family": "ahem", "font-style": "normal", "margin-bottom": "16px", "margin-left": "0px", @@ -806,6 +849,7 @@ class DomTreeExtractorBrowserTest : public HeadlessAsyncDevTooledBrowserTest, R"raw_string({ "color": "rgb(0, 128, 0)", "display": "block", + "font-family": "ahem", "font-style": "normal", "margin-bottom": "0px", "margin-left": "0px", @@ -816,12 +860,14 @@ class DomTreeExtractorBrowserTest : public HeadlessAsyncDevTooledBrowserTest, R"raw_string({ "color": "rgb(0, 128, 0)", "display": "inline", + "font-family": "ahem", "font-style": "italic", "margin-bottom": "0px", "margin-left": "0px", "margin-right": "0px", "margin-top": "0px" - })raw_string"}; + } + )raw_string"}; for (size_t i = 0; i < computed_styles.size(); i++) { std::string result_json; diff --git a/chromium/headless/public/util/http_url_fetcher.h b/chromium/headless/public/util/http_url_fetcher.h index 2b17287d8fd..aa9cdb650f6 100644 --- a/chromium/headless/public/util/http_url_fetcher.h +++ b/chromium/headless/public/util/http_url_fetcher.h @@ -10,7 +10,6 @@ namespace net { class URLRequestContext; -class URLRequestJobFactory; } // namespace namespace headless { diff --git a/chromium/headless/public/util/in_memory_request_job.h b/chromium/headless/public/util/in_memory_request_job.h index 1526c9a470c..7810422b27f 100644 --- a/chromium/headless/public/util/in_memory_request_job.h +++ b/chromium/headless/public/util/in_memory_request_job.h @@ -9,11 +9,6 @@ #include "headless/public/util/in_memory_protocol_handler.h" #include "net/url_request/url_request_job.h" -namespace net { -class StringIOBuffer; -class DrainableIOBuffer; -} - namespace headless { class InMemoryRequestJob : public net::URLRequestJob { public: 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 08ff33d4b07..adcabbef839 100644 --- a/chromium/headless/public/util/testing/generic_url_request_mocks.cc +++ b/chromium/headless/public/util/testing/generic_url_request_mocks.cc @@ -5,6 +5,7 @@ #include "headless/public/util/testing/generic_url_request_mocks.h" #include "base/logging.h" +#include "base/threading/thread_task_runner_handle.h" namespace net { class URLRequestJob; 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 7dc683fce64..fb8f0bef954 100644 --- a/chromium/headless/public/util/testing/generic_url_request_mocks.h +++ b/chromium/headless/public/util/testing/generic_url_request_mocks.h @@ -16,14 +16,6 @@ #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_job_factory.h" -namespace net { -class URLRequestJob; -} // namespace net - -namespace htmlrender_webkit_headless_proto { -class Resource; -} // htmlrender_webkit_headless_proto net - namespace headless { class MockGenericURLRequestJobDelegate : public GenericURLRequestJob::Delegate { |