summaryrefslogtreecommitdiff
path: root/chromium/headless
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@theqtcompany.com>2016-07-14 17:41:05 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2016-08-04 12:37:36 +0000
commit399c965b6064c440ddcf4015f5f8e9d131c7a0a6 (patch)
tree6b06b60ff365abef0e13b3503d593a0df48d20e8 /chromium/headless
parent7366110654eec46f21b6824f302356426f48cd74 (diff)
downloadqtwebengine-chromium-399c965b6064c440ddcf4015f5f8e9d131c7a0a6.tar.gz
BASELINE: Update Chromium to 52.0.2743.76 and Ninja to 1.7.1
Change-Id: I382f51b959689505a60f8b707255ecb344f7d8b4 Reviewed-by: Michael BrĂ¼ning <michael.bruning@qt.io>
Diffstat (limited to 'chromium/headless')
-rw-r--r--chromium/headless/BUILD.gn187
-rw-r--r--chromium/headless/DEPS1
-rw-r--r--chromium/headless/README.md9
-rw-r--r--chromium/headless/app/headless_shell.cc189
-rw-r--r--chromium/headless/app/headless_shell_switches.cc13
-rw-r--r--chromium/headless/app/headless_shell_switches.h3
-rw-r--r--chromium/headless/lib/browser/client_api_generator.py422
-rw-r--r--chromium/headless/lib/browser/client_api_generator_unittest.py534
-rw-r--r--chromium/headless/lib/browser/domain_cc.template142
-rw-r--r--chromium/headless/lib/browser/domain_h.template158
-rw-r--r--chromium/headless/lib/browser/headless_browser_context.cc37
-rw-r--r--chromium/headless/lib/browser/headless_browser_context.h11
-rw-r--r--chromium/headless/lib/browser/headless_browser_impl.cc48
-rw-r--r--chromium/headless/lib/browser/headless_browser_impl.h22
-rw-r--r--chromium/headless/lib/browser/headless_browser_main_parts.cc5
-rw-r--r--chromium/headless/lib/browser/headless_devtools_client_impl.cc346
-rw-r--r--chromium/headless/lib/browser/headless_devtools_client_impl.h189
-rw-r--r--chromium/headless/lib/browser/headless_screen.cc38
-rw-r--r--chromium/headless/lib/browser/headless_screen.h35
-rw-r--r--chromium/headless/lib/browser/headless_url_request_context_getter.cc9
-rw-r--r--chromium/headless/lib/browser/headless_web_contents_impl.cc90
-rw-r--r--chromium/headless/lib/browser/headless_web_contents_impl.h40
-rw-r--r--chromium/headless/lib/browser/headless_window_tree_client.cc27
-rw-r--r--chromium/headless/lib/browser/headless_window_tree_client.h30
-rw-r--r--chromium/headless/lib/browser/type_conversions_h.template77
-rw-r--r--chromium/headless/lib/browser/types_cc.template130
-rw-r--r--chromium/headless/lib/browser/types_h.template158
-rw-r--r--chromium/headless/lib/headless_browser_browsertest.cc43
-rw-r--r--chromium/headless/lib/headless_content_main_delegate.cc4
-rw-r--r--chromium/headless/lib/headless_devtools_client_browsertest.cc213
-rw-r--r--chromium/headless/lib/headless_web_contents_browsertest.cc45
-rw-r--r--chromium/headless/public/domains/README.md3
-rw-r--r--chromium/headless/public/domains/types_unittest.cc200
-rw-r--r--chromium/headless/public/headless_browser.cc13
-rw-r--r--chromium/headless/public/headless_browser.h21
-rw-r--r--chromium/headless/public/headless_devtools_client.h157
-rw-r--r--chromium/headless/public/headless_devtools_target.h37
-rw-r--r--chromium/headless/public/headless_web_contents.h21
-rw-r--r--chromium/headless/public/internal/message_dispatcher.h35
-rw-r--r--chromium/headless/public/internal/value_conversions.h163
-rw-r--r--chromium/headless/public/util/error_reporter.cc51
-rw-r--r--chromium/headless/public/util/error_reporter.h48
-rw-r--r--chromium/headless/public/util/error_reporter_unittest.cc52
-rw-r--r--chromium/headless/public/util/maybe.h92
-rw-r--r--chromium/headless/public/util/maybe_unittest.cc83
45 files changed, 3867 insertions, 364 deletions
diff --git a/chromium/headless/BUILD.gn b/chromium/headless/BUILD.gn
index c035c2599f4..e1a853ee47c 100644
--- a/chromium/headless/BUILD.gn
+++ b/chromium/headless/BUILD.gn
@@ -54,8 +54,156 @@ grit("headless_lib_resources_grit") {
]
}
+action("gen_devtools_client_api") {
+ script = "//headless/lib/browser/client_api_generator.py"
+
+ inputs = [
+ "//third_party/WebKit/Source/devtools/protocol.json",
+ ]
+
+ outputs = [
+ "$target_gen_dir/public/domains/accessibility.cc",
+ "$target_gen_dir/public/domains/accessibility.h",
+ "$target_gen_dir/public/domains/animation.cc",
+ "$target_gen_dir/public/domains/animation.h",
+ "$target_gen_dir/public/domains/application_cache.cc",
+ "$target_gen_dir/public/domains/application_cache.h",
+ "$target_gen_dir/public/domains/cache_storage.cc",
+ "$target_gen_dir/public/domains/cache_storage.h",
+ "$target_gen_dir/public/domains/console.cc",
+ "$target_gen_dir/public/domains/console.h",
+ "$target_gen_dir/public/domains/css.cc",
+ "$target_gen_dir/public/domains/css.h",
+ "$target_gen_dir/public/domains/database.cc",
+ "$target_gen_dir/public/domains/database.h",
+ "$target_gen_dir/public/domains/debugger.cc",
+ "$target_gen_dir/public/domains/debugger.h",
+ "$target_gen_dir/public/domains/device_orientation.cc",
+ "$target_gen_dir/public/domains/device_orientation.h",
+ "$target_gen_dir/public/domains/dom_debugger.cc",
+ "$target_gen_dir/public/domains/dom_debugger.h",
+ "$target_gen_dir/public/domains/dom.cc",
+ "$target_gen_dir/public/domains/dom.h",
+ "$target_gen_dir/public/domains/dom_storage.cc",
+ "$target_gen_dir/public/domains/dom_storage.h",
+ "$target_gen_dir/public/domains/emulation.cc",
+ "$target_gen_dir/public/domains/emulation.h",
+ "$target_gen_dir/public/domains/heap_profiler.cc",
+ "$target_gen_dir/public/domains/heap_profiler.h",
+ "$target_gen_dir/public/domains/indexeddb.cc",
+ "$target_gen_dir/public/domains/indexeddb.h",
+ "$target_gen_dir/public/domains/input.cc",
+ "$target_gen_dir/public/domains/input.h",
+ "$target_gen_dir/public/domains/inspector.cc",
+ "$target_gen_dir/public/domains/inspector.h",
+ "$target_gen_dir/public/domains/io.cc",
+ "$target_gen_dir/public/domains/io.h",
+ "$target_gen_dir/public/domains/layer_tree.cc",
+ "$target_gen_dir/public/domains/layer_tree.h",
+ "$target_gen_dir/public/domains/memory.cc",
+ "$target_gen_dir/public/domains/memory.h",
+ "$target_gen_dir/public/domains/network.cc",
+ "$target_gen_dir/public/domains/network.h",
+ "$target_gen_dir/public/domains/page.cc",
+ "$target_gen_dir/public/domains/page.h",
+ "$target_gen_dir/public/domains/profiler.cc",
+ "$target_gen_dir/public/domains/profiler.h",
+ "$target_gen_dir/public/domains/rendering.cc",
+ "$target_gen_dir/public/domains/rendering.h",
+ "$target_gen_dir/public/domains/runtime.cc",
+ "$target_gen_dir/public/domains/runtime.h",
+ "$target_gen_dir/public/domains/security.cc",
+ "$target_gen_dir/public/domains/security.h",
+ "$target_gen_dir/public/domains/service_worker.cc",
+ "$target_gen_dir/public/domains/service_worker.h",
+ "$target_gen_dir/public/domains/tracing.cc",
+ "$target_gen_dir/public/domains/tracing.h",
+ "$target_gen_dir/public/domains/type_conversions.h",
+ "$target_gen_dir/public/domains/types.cc",
+ "$target_gen_dir/public/domains/types.h",
+ "$target_gen_dir/public/domains/worker.cc",
+ "$target_gen_dir/public/domains/worker.h",
+ ]
+
+ sources = [
+ "lib/browser/domain_cc.template",
+ "lib/browser/domain_h.template",
+ "lib/browser/type_conversions_h.template",
+ "lib/browser/types_cc.template",
+ "lib/browser/types_h.template",
+ ]
+
+ args = [
+ "--protocol",
+ rebase_path(inputs[0], root_build_dir),
+ "--output_dir",
+ rebase_path(target_gen_dir) + "/public/domains",
+ ]
+}
+
static_library("headless_lib") {
sources = [
+ "$target_gen_dir/public/domains/accessibility.cc",
+ "$target_gen_dir/public/domains/accessibility.h",
+ "$target_gen_dir/public/domains/animation.cc",
+ "$target_gen_dir/public/domains/animation.h",
+ "$target_gen_dir/public/domains/application_cache.cc",
+ "$target_gen_dir/public/domains/application_cache.h",
+ "$target_gen_dir/public/domains/cache_storage.cc",
+ "$target_gen_dir/public/domains/cache_storage.h",
+ "$target_gen_dir/public/domains/console.cc",
+ "$target_gen_dir/public/domains/console.h",
+ "$target_gen_dir/public/domains/css.cc",
+ "$target_gen_dir/public/domains/css.h",
+ "$target_gen_dir/public/domains/database.cc",
+ "$target_gen_dir/public/domains/database.h",
+ "$target_gen_dir/public/domains/debugger.cc",
+ "$target_gen_dir/public/domains/debugger.h",
+ "$target_gen_dir/public/domains/device_orientation.cc",
+ "$target_gen_dir/public/domains/device_orientation.h",
+ "$target_gen_dir/public/domains/dom.cc",
+ "$target_gen_dir/public/domains/dom.h",
+ "$target_gen_dir/public/domains/dom_debugger.cc",
+ "$target_gen_dir/public/domains/dom_debugger.h",
+ "$target_gen_dir/public/domains/dom_storage.cc",
+ "$target_gen_dir/public/domains/dom_storage.h",
+ "$target_gen_dir/public/domains/emulation.cc",
+ "$target_gen_dir/public/domains/emulation.h",
+ "$target_gen_dir/public/domains/heap_profiler.cc",
+ "$target_gen_dir/public/domains/heap_profiler.h",
+ "$target_gen_dir/public/domains/indexeddb.cc",
+ "$target_gen_dir/public/domains/indexeddb.h",
+ "$target_gen_dir/public/domains/input.cc",
+ "$target_gen_dir/public/domains/input.h",
+ "$target_gen_dir/public/domains/inspector.cc",
+ "$target_gen_dir/public/domains/inspector.h",
+ "$target_gen_dir/public/domains/io.cc",
+ "$target_gen_dir/public/domains/io.h",
+ "$target_gen_dir/public/domains/layer_tree.cc",
+ "$target_gen_dir/public/domains/layer_tree.h",
+ "$target_gen_dir/public/domains/memory.cc",
+ "$target_gen_dir/public/domains/memory.h",
+ "$target_gen_dir/public/domains/network.cc",
+ "$target_gen_dir/public/domains/network.h",
+ "$target_gen_dir/public/domains/page.cc",
+ "$target_gen_dir/public/domains/page.h",
+ "$target_gen_dir/public/domains/profiler.cc",
+ "$target_gen_dir/public/domains/profiler.h",
+ "$target_gen_dir/public/domains/rendering.cc",
+ "$target_gen_dir/public/domains/rendering.h",
+ "$target_gen_dir/public/domains/runtime.cc",
+ "$target_gen_dir/public/domains/runtime.h",
+ "$target_gen_dir/public/domains/security.cc",
+ "$target_gen_dir/public/domains/security.h",
+ "$target_gen_dir/public/domains/service_worker.cc",
+ "$target_gen_dir/public/domains/service_worker.h",
+ "$target_gen_dir/public/domains/tracing.cc",
+ "$target_gen_dir/public/domains/tracing.h",
+ "$target_gen_dir/public/domains/type_conversions.h",
+ "$target_gen_dir/public/domains/types.cc",
+ "$target_gen_dir/public/domains/types.h",
+ "$target_gen_dir/public/domains/worker.cc",
+ "$target_gen_dir/public/domains/worker.h",
"lib/browser/headless_browser_context.cc",
"lib/browser/headless_browser_context.h",
"lib/browser/headless_browser_impl.cc",
@@ -66,12 +214,16 @@ static_library("headless_lib") {
"lib/browser/headless_content_browser_client.h",
"lib/browser/headless_devtools.cc",
"lib/browser/headless_devtools.h",
+ "lib/browser/headless_devtools_client_impl.cc",
+ "lib/browser/headless_devtools_client_impl.h",
"lib/browser/headless_screen.cc",
"lib/browser/headless_screen.h",
"lib/browser/headless_url_request_context_getter.cc",
"lib/browser/headless_url_request_context_getter.h",
"lib/browser/headless_web_contents_impl.cc",
"lib/browser/headless_web_contents_impl.h",
+ "lib/browser/headless_window_tree_client.cc",
+ "lib/browser/headless_window_tree_client.h",
"lib/headless_content_client.cc",
"lib/headless_content_client.h",
"lib/headless_content_main_delegate.cc",
@@ -82,12 +234,19 @@ static_library("headless_lib") {
"lib/utility/headless_content_utility_client.h",
"public/headless_browser.cc",
"public/headless_browser.h",
+ "public/headless_devtools_client.h",
+ "public/headless_devtools_host.h",
+ "public/headless_devtools_target.h",
"public/headless_export.h",
"public/headless_web_contents.h",
- "public/util/maybe.h",
+ "public/internal/message_dispatcher.h",
+ "public/internal/value_conversions.h",
+ "public/util/error_reporter.cc",
+ "public/util/error_reporter.h",
]
deps = [
+ ":gen_devtools_client_api",
":pak",
"//base",
"//components/devtools_http_handler",
@@ -101,6 +260,7 @@ static_library("headless_lib") {
"//ui/aura",
"//ui/base",
"//ui/compositor",
+ "//ui/display",
"//ui/ozone",
"//url",
]
@@ -110,6 +270,7 @@ group("headless_tests") {
testonly = true
deps = [
+ ":client_api_generator_tests",
":headless_browsertests",
":headless_unittests",
]
@@ -117,19 +278,39 @@ group("headless_tests") {
test("headless_unittests") {
sources = [
- "public/util/maybe_unittest.cc",
+ "public/domains/types_unittest.cc",
+ "public/util/error_reporter_unittest.cc",
]
deps = [
+ ":headless_lib",
"//base/test:run_all_unittests",
"//base/test:test_support",
"//testing/gtest",
]
}
+action("client_api_generator_tests") {
+ _stamp = "$target_gen_dir/client_api_generator_unittests.stamp"
+ inputs = [
+ "lib/browser/client_api_generator.py",
+ "lib/browser/client_api_generator_unittest.py",
+ ]
+ outputs = [
+ _stamp,
+ ]
+
+ script = "lib/browser/client_api_generator_unittest.py"
+ args = [
+ "--stamp",
+ rebase_path(_stamp, root_build_dir),
+ ]
+}
+
test("headless_browsertests") {
sources = [
"lib/headless_browser_browsertest.cc",
+ "lib/headless_devtools_client_browsertest.cc",
"lib/headless_web_contents_browsertest.cc",
"test/headless_browser_test.cc",
"test/headless_browser_test.h",
@@ -149,8 +330,6 @@ test("headless_browsertests") {
}
executable("headless_shell") {
- testonly = true
-
sources = [
"app/headless_shell.cc",
"app/headless_shell_switches.cc",
diff --git a/chromium/headless/DEPS b/chromium/headless/DEPS
index b251cd96553..2e747057cbf 100644
--- a/chromium/headless/DEPS
+++ b/chromium/headless/DEPS
@@ -4,6 +4,7 @@ include_rules = [
"+net",
"+ui/base",
"+ui/base/resource",
+ "+ui/display",
"+ui/gfx",
"+ui/gfx/geometry",
"+ui/ozone/public",
diff --git a/chromium/headless/README.md b/chromium/headless/README.md
index 0748cfd8f6a..9e3c7b02eba 100644
--- a/chromium/headless/README.md
+++ b/chromium/headless/README.md
@@ -5,6 +5,9 @@ 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.
+
## Headless shell
The headless shell is a sample application which demonstrates the use of the
@@ -52,10 +55,14 @@ The main embedder API classes are:
- `SetProxyServer` - Configures an HTTP/HTTPS proxy server to be used for
accessing the network.
-## Client API
+## Client/DevTools API
The headless client API is used to drive the browser and interact with loaded
web pages. Its main classes are:
- `HeadlessBrowser` - Represents the global headless browser instance.
- `HeadlessWebContents` - Represents a single "tab" within the browser.
+- `HeadlessDevToolsClient` - Provides a C++ interface for inspecting and
+ controlling a tab. The API functions corresponds to [DevTools commands](https://developer.chrome.com/devtools/docs/debugger-protocol).
+ See the [client API documentation](https://docs.google.com/document/d/1rlqcp8nk-ZQvldNJWdbaMbwfDbJoOXvahPCDoPGOwhQ/edit#)
+ for more information.
diff --git a/chromium/headless/app/headless_shell.cc b/chromium/headless/app/headless_shell.cc
index 1031dbd5446..25c823d541c 100644
--- a/chromium/headless/app/headless_shell.cc
+++ b/chromium/headless/app/headless_shell.cc
@@ -2,24 +2,33 @@
// 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 "base/bind.h"
#include "base/callback.h"
#include "base/command_line.h"
+#include "base/json/json_writer.h"
#include "base/location.h"
#include "base/memory/ref_counted.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/domains/page.h"
+#include "headless/public/domains/runtime.h"
#include "headless/public/headless_browser.h"
+#include "headless/public/headless_devtools_client.h"
+#include "headless/public/headless_devtools_target.h"
#include "headless/public/headless_web_contents.h"
#include "net/base/ip_address.h"
#include "ui/gfx/geometry/size.h"
using headless::HeadlessBrowser;
+using headless::HeadlessDevToolsClient;
using headless::HeadlessWebContents;
+namespace page = headless::page;
+namespace runtime = headless::runtime;
namespace {
// Address where to listen to incoming DevTools connections.
@@ -27,13 +36,13 @@ const char kDevToolsHttpServerAddress[] = "127.0.0.1";
}
// A sample application which demonstrates the use of the headless API.
-class HeadlessShell : public HeadlessWebContents::Observer {
+class HeadlessShell : public HeadlessWebContents::Observer, page::Observer {
public:
- HeadlessShell() : browser_(nullptr) {}
- ~HeadlessShell() override {
- if (web_contents_)
- web_contents_->RemoveObserver(this);
- }
+ HeadlessShell()
+ : browser_(nullptr),
+ devtools_client_(HeadlessDevToolsClient::Create()),
+ processed_page_ready_(false) {}
+ ~HeadlessShell() override {}
void OnStart(HeadlessBrowser* browser) {
browser_ = browser;
@@ -42,13 +51,13 @@ class HeadlessShell : public HeadlessWebContents::Observer {
base::CommandLine::ForCurrentProcess()->GetArgs();
const char kDefaultUrl[] = "about:blank";
- GURL url;
if (args.empty() || args[0].empty()) {
- url = GURL(kDefaultUrl);
+ url_ = GURL(kDefaultUrl);
} else {
- url = GURL(args[0]);
+ url_ = GURL(args[0]);
}
- web_contents_ = browser->CreateWebContents(url, gfx::Size(800, 600));
+
+ web_contents_ = browser->CreateWebContents(url_, gfx::Size(800, 600));
if (!web_contents_) {
LOG(ERROR) << "Navigation failed";
browser_->Shutdown();
@@ -57,23 +66,133 @@ class HeadlessShell : public HeadlessWebContents::Observer {
web_contents_->AddObserver(this);
}
- void ShutdownIfNeeded() {
- const base::CommandLine& command_line =
- *base::CommandLine::ForCurrentProcess();
- if (!command_line.HasSwitch(switches::kRemoteDebuggingPort)) {
- web_contents_ = nullptr;
- browser_->Shutdown();
+ void Shutdown() {
+ if (!web_contents_)
+ return;
+ if (!RemoteDebuggingEnabled()) {
+ devtools_client_->GetPage()->RemoveObserver(this);
+ web_contents_->GetDevToolsTarget()->DetachClient(devtools_client_.get());
}
+ web_contents_->RemoveObserver(this);
+ web_contents_ = nullptr;
+ browser_->Shutdown();
}
// HeadlessWebContents::Observer implementation:
- void DocumentOnLoadCompletedInMainFrame() override {
- ShutdownIfNeeded();
+ void DevToolsTargetReady() override {
+ if (RemoteDebuggingEnabled())
+ return;
+ web_contents_->GetDevToolsTarget()->AttachClient(devtools_client_.get());
+ devtools_client_->GetPage()->AddObserver(this);
+ devtools_client_->GetPage()->Enable();
+ // Check if the document had already finished loading by the time we
+ // attached.
+ PollReadyState();
+ // TODO(skyostil): Implement more features to demonstrate the devtools API.
+ }
+
+ 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)));
+ }
+
+ void OnReadyState(std::unique_ptr<runtime::EvaluateResult> result) {
+ std::string ready_state_and_url;
+ if (result->GetResult()->GetValue()->GetAsString(&ready_state_and_url)) {
+ std::stringstream stream(ready_state_and_url);
+ std::string ready_state;
+ std::string url;
+ stream >> ready_state;
+ stream >> url;
+
+ if (ready_state == "complete" &&
+ (url_.spec() == url || url != "about:blank")) {
+ OnPageReady();
+ return;
+ }
+ }
+ }
+
+ // page::Observer implementation:
+ void OnLoadEventFired(const page::LoadEventFiredParams& params) override {
+ OnPageReady();
+ }
+
+ void OnPageReady() {
+ if (processed_page_ready_)
+ return;
+ processed_page_ready_ = true;
+
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+ headless::switches::kDumpDom)) {
+ FetchDom();
+ } else if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+ headless::switches::kRepl)) {
+ std::cout
+ << "Type a Javascript expression to evaluate or \"quit\" to exit."
+ << std::endl;
+ InputExpression();
+ } else {
+ Shutdown();
+ }
+ }
+
+ void FetchDom() {
+ devtools_client_->GetRuntime()->Evaluate(
+ "document.body.innerHTML",
+ base::Bind(&HeadlessShell::OnDomFetched, base::Unretained(this)));
+ }
+
+ void OnDomFetched(std::unique_ptr<runtime::EvaluateResult> result) {
+ if (result->GetWasThrown()) {
+ LOG(ERROR) << "Failed to evaluate document.body.innerHTML";
+ } else {
+ std::string dom;
+ if (result->GetResult()->GetValue()->GetAsString(&dom)) {
+ std::cout << dom << std::endl;
+ }
+ }
+ Shutdown();
+ }
+
+ 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") {
+ Shutdown();
+ return;
+ }
+ devtools_client_->GetRuntime()->Evaluate(
+ expression,
+ base::Bind(&HeadlessShell::OnExpressionResult, base::Unretained(this)));
+ }
+
+ 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;
+ InputExpression();
+ }
+
+ bool RemoteDebuggingEnabled() const {
+ const base::CommandLine& command_line =
+ *base::CommandLine::ForCurrentProcess();
+ return command_line.HasSwitch(switches::kRemoteDebuggingPort);
}
private:
+ GURL url_;
HeadlessBrowser* browser_; // Not owned.
- std::unique_ptr<HeadlessWebContents> web_contents_;
+ std::unique_ptr<HeadlessDevToolsClient> devtools_client_;
+ HeadlessWebContents* web_contents_;
+ bool processed_page_ready_;
DISALLOW_COPY_AND_ASSIGN(HeadlessShell);
};
@@ -85,18 +204,29 @@ int main(int argc, const char** argv) {
// Enable devtools if requested.
base::CommandLine command_line(argc, argv);
if (command_line.HasSwitch(switches::kRemoteDebuggingPort)) {
+ std::string address = kDevToolsHttpServerAddress;
+ if (command_line.HasSwitch(headless::switches::kRemoteDebuggingAddress)) {
+ address = command_line.GetSwitchValueASCII(
+ headless::switches::kRemoteDebuggingAddress);
+ net::IPAddress parsed_address;
+ if (!net::ParseURLHostnameToAddress(address, &parsed_address)) {
+ LOG(ERROR) << "Invalid devtools server address";
+ return EXIT_FAILURE;
+ }
+ }
int parsed_port;
std::string port_str =
command_line.GetSwitchValueASCII(switches::kRemoteDebuggingPort);
- if (base::StringToInt(port_str, &parsed_port) &&
- base::IsValueInRangeForNumericType<uint16_t>(parsed_port)) {
- net::IPAddress devtools_address;
- bool result =
- devtools_address.AssignFromIPLiteral(kDevToolsHttpServerAddress);
- DCHECK(result);
- builder.EnableDevToolsServer(net::IPEndPoint(
- devtools_address, base::checked_cast<uint16_t>(parsed_port)));
+ if (!base::StringToInt(port_str, &parsed_port) ||
+ !base::IsValueInRangeForNumericType<uint16_t>(parsed_port)) {
+ LOG(ERROR) << "Invalid devtools server port";
+ return EXIT_FAILURE;
}
+ net::IPAddress devtools_address;
+ bool result = devtools_address.AssignFromIPLiteral(address);
+ DCHECK(result);
+ builder.EnableDevToolsServer(net::IPEndPoint(
+ devtools_address, base::checked_cast<uint16_t>(parsed_port)));
}
if (command_line.HasSwitch(headless::switches::kProxyServer)) {
@@ -111,6 +241,11 @@ int main(int argc, const char** argv) {
builder.SetProxyServer(parsed_proxy_server);
}
+ if (command_line.HasSwitch(switches::kHostResolverRules)) {
+ builder.SetHostResolverRules(
+ command_line.GetSwitchValueASCII(switches::kHostResolverRules));
+ }
+
return HeadlessBrowserMain(
builder.Build(),
base::Bind(&HeadlessShell::OnStart, base::Unretained(&shell)));
diff --git a/chromium/headless/app/headless_shell_switches.cc b/chromium/headless/app/headless_shell_switches.cc
index c3c6ad7b221..f8ee4343376 100644
--- a/chromium/headless/app/headless_shell_switches.cc
+++ b/chromium/headless/app/headless_shell_switches.cc
@@ -7,9 +7,22 @@
namespace headless {
namespace switches {
+// Instructs headless_shell to print document.body.innerHTML to stdout.
+const char kDumpDom[] = "dump-dom";
+
// Uses a specified proxy server, overrides system settings. This switch only
// affects HTTP and HTTPS requests.
const char kProxyServer[] = "proxy-server";
+// Use the given address instead of the default loopback for accepting remote
+// debugging connections. Should be used together with --remote-debugging-port.
+// Note that the remote debugging protocol does not perform any authentication,
+// so exposing it too widely can be a security risk.
+const char kRemoteDebuggingAddress[] = "remote-debugging-address";
+
+// Runs a read-eval-print loop that allows the user to evaluate Javascript
+// expressions.
+const char kRepl[] = "repl";
+
} // namespace switches
} // namespace headless
diff --git a/chromium/headless/app/headless_shell_switches.h b/chromium/headless/app/headless_shell_switches.h
index dbca97c9d3b..d4a8c6bf7f7 100644
--- a/chromium/headless/app/headless_shell_switches.h
+++ b/chromium/headless/app/headless_shell_switches.h
@@ -7,7 +7,10 @@
namespace headless {
namespace switches {
+extern const char kDumpDom[];
extern const char kProxyServer[];
+extern const char kRemoteDebuggingAddress[];
+extern const char kRepl[];
} // namespace switches
} // namespace headless
diff --git a/chromium/headless/lib/browser/client_api_generator.py b/chromium/headless/lib/browser/client_api_generator.py
new file mode 100644
index 00000000000..40f1206d17b
--- /dev/null
+++ b/chromium/headless/lib/browser/client_api_generator.py
@@ -0,0 +1,422 @@
+# 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.
+
+import argparse
+import os.path
+import sys
+import re
+try:
+ import json
+except ImportError:
+ import simplejson as json
+
+# Path handling for libraries and templates
+# Paths have to be normalized because Jinja uses the exact template path to
+# determine the hash used in the cache filename, and we need a pre-caching step
+# to be concurrency-safe. Use absolute path because __file__ is absolute if
+# module is imported, and relative if executed directly.
+# If paths differ between pre-caching and individual file compilation, the cache
+# is regenerated, which causes a race condition and breaks concurrent build,
+# since some compile processes will try to read the partially written cache.
+module_path, module_filename = os.path.split(os.path.realpath(__file__))
+third_party_dir = os.path.normpath(os.path.join(
+ module_path, os.pardir, os.pardir, os.pardir, 'third_party'))
+templates_dir = module_path
+
+# jinja2 is in chromium's third_party directory.
+# Insert at 1 so at front to override system libraries, and
+# after path[0] == invoking script dir
+sys.path.insert(1, third_party_dir)
+import jinja2
+
+
+def ParseArguments(args):
+ """Parses command line arguments and returns a (json_api, output_dir) tuple.
+ """
+ cmdline_parser = argparse.ArgumentParser()
+ cmdline_parser.add_argument('--protocol', required=True)
+ cmdline_parser.add_argument('--output_dir', required=True)
+
+ args = cmdline_parser.parse_args(args)
+ with open(args.protocol, 'r') as f:
+ return json.load(f), args.output_dir
+
+
+def ToTitleCase(name):
+ return name[:1].upper() + name[1:]
+
+
+def DashToCamelCase(word):
+ return ''.join(ToTitleCase(x) for x in word.split('-'))
+
+
+def CamelCaseToHackerStyle(name):
+ # Do two passes to insert '_' chars to deal with overlapping matches (e.g.,
+ # 'LoLoLoL').
+ name = re.sub(r'([^_])([A-Z][a-z]+?)', r'\1_\2', name)
+ name = re.sub(r'([^_])([A-Z][a-z]+?)', r'\1_\2', name)
+ return name.lower()
+
+
+def MangleEnum(value):
+ # Rename NULL enumeration values to avoid a clash with the actual NULL.
+ return 'NONE' if value == 'NULL' else value
+
+
+def InitializeJinjaEnv(cache_dir):
+ jinja_env = jinja2.Environment(
+ loader=jinja2.FileSystemLoader(templates_dir),
+ # Bytecode cache is not concurrency-safe unless pre-cached:
+ # if pre-cached this is read-only, but writing creates a race condition.
+ bytecode_cache=jinja2.FileSystemBytecodeCache(cache_dir),
+ keep_trailing_newline=True, # Newline-terminate generated files.
+ lstrip_blocks=True, # So we can indent control flow tags.
+ trim_blocks=True)
+ jinja_env.filters.update({
+ 'to_title_case': ToTitleCase,
+ 'dash_to_camelcase': DashToCamelCase,
+ 'camelcase_to_hacker_style': CamelCaseToHackerStyle,
+ 'mangle_enum': MangleEnum,
+ })
+ jinja_env.add_extension('jinja2.ext.loopcontrols')
+ return jinja_env
+
+
+def PatchFullQualifiedRefs(json_api):
+ def PatchFullQualifiedRefsInDomain(json, domain_name):
+ if isinstance(json, list):
+ for item in json:
+ PatchFullQualifiedRefsInDomain(item, domain_name)
+
+ if not isinstance(json, dict):
+ return
+ for key in json:
+ if key != '$ref':
+ PatchFullQualifiedRefsInDomain(json[key], domain_name)
+ continue
+ if not '.' in json['$ref']:
+ json['$ref'] = domain_name + '.' + json['$ref']
+
+ for domain in json_api['domains']:
+ PatchFullQualifiedRefsInDomain(domain, domain['domain'])
+
+
+def CreateUserTypeDefinition(domain, type):
+ namespace = CamelCaseToHackerStyle(domain['domain'])
+ return {
+ 'return_type': 'std::unique_ptr<headless::%s::%s>' % (
+ namespace, type['id']),
+ 'pass_type': 'std::unique_ptr<headless::%s::%s>' % (namespace, type['id']),
+ 'to_raw_type': '*%s',
+ 'to_raw_return_type': '%s.get()',
+ 'to_pass_type': 'std::move(%s)',
+ 'type': 'std::unique_ptr<headless::%s::%s>' % (namespace, type['id']),
+ 'raw_type': 'headless::%s::%s' % (namespace, type['id']),
+ 'raw_pass_type': 'headless::%s::%s*' % (namespace, type['id']),
+ 'raw_return_type': 'const headless::%s::%s*' % (namespace, type['id']),
+ }
+
+
+def CreateEnumTypeDefinition(domain_name, type):
+ namespace = CamelCaseToHackerStyle(domain_name)
+ return {
+ 'return_type': 'headless::%s::%s' % (namespace, type['id']),
+ 'pass_type': 'headless::%s::%s' % (namespace, type['id']),
+ 'to_raw_type': '%s',
+ 'to_raw_return_type': '%s',
+ 'to_pass_type': '%s',
+ 'type': 'headless::%s::%s' % (namespace, type['id']),
+ 'raw_type': 'headless::%s::%s' % (namespace, type['id']),
+ 'raw_pass_type': 'headless::%s::%s' % (namespace, type['id']),
+ 'raw_return_type': 'headless::%s::%s' % (namespace, type['id']),
+ }
+
+
+def CreateObjectTypeDefinition():
+ return {
+ 'return_type': 'std::unique_ptr<base::DictionaryValue>',
+ 'pass_type': 'std::unique_ptr<base::DictionaryValue>',
+ 'to_raw_type': '*%s',
+ 'to_raw_return_type': '%s.get()',
+ 'to_pass_type': 'std::move(%s)',
+ 'type': 'std::unique_ptr<base::DictionaryValue>',
+ 'raw_type': 'base::DictionaryValue',
+ 'raw_pass_type': 'base::DictionaryValue*',
+ 'raw_return_type': 'const base::DictionaryValue*',
+ }
+
+
+def WrapObjectTypeDefinition(type):
+ id = type.get('id', 'base::Value')
+ return {
+ 'return_type': 'std::unique_ptr<%s>' % id,
+ 'pass_type': 'std::unique_ptr<%s>' % id,
+ 'to_raw_type': '*%s',
+ 'to_raw_return_type': '%s.get()',
+ 'to_pass_type': 'std::move(%s)',
+ 'type': 'std::unique_ptr<%s>' % id,
+ 'raw_type': id,
+ 'raw_pass_type': '%s*' % id,
+ 'raw_return_type': 'const %s*' % id,
+ }
+
+
+def CreateAnyTypeDefinition():
+ return {
+ 'return_type': 'std::unique_ptr<base::Value>',
+ 'pass_type': 'std::unique_ptr<base::Value>',
+ 'to_raw_type': '*%s',
+ 'to_raw_return_type': '%s.get()',
+ 'to_pass_type': 'std::move(%s)',
+ 'type': 'std::unique_ptr<base::Value>',
+ 'raw_type': 'base::Value',
+ 'raw_pass_type': 'base::Value*',
+ 'raw_return_type': 'const base::Value*',
+ }
+
+
+def CreateStringTypeDefinition(domain):
+ return {
+ 'return_type': 'std::string',
+ 'pass_type': 'const std::string&',
+ 'to_pass_type': '%s',
+ 'to_raw_type': '%s',
+ 'to_raw_return_type': '%s',
+ 'type': 'std::string',
+ 'raw_type': 'std::string',
+ 'raw_pass_type': 'const std::string&',
+ 'raw_return_type': 'std::string',
+ }
+
+
+def CreatePrimitiveTypeDefinition(type):
+ typedefs = {
+ 'number': 'double',
+ 'integer': 'int',
+ 'boolean': 'bool',
+ 'string': 'std::string',
+ }
+ return {
+ 'return_type': typedefs[type],
+ 'pass_type': typedefs[type],
+ 'to_pass_type': '%s',
+ 'to_raw_type': '%s',
+ 'to_raw_return_type': '%s',
+ 'type': typedefs[type],
+ 'raw_type': typedefs[type],
+ 'raw_pass_type': typedefs[type],
+ 'raw_return_type': typedefs[type],
+ }
+
+
+type_definitions = {}
+type_definitions['number'] = CreatePrimitiveTypeDefinition('number')
+type_definitions['integer'] = CreatePrimitiveTypeDefinition('integer')
+type_definitions['boolean'] = CreatePrimitiveTypeDefinition('boolean')
+type_definitions['string'] = CreatePrimitiveTypeDefinition('string')
+type_definitions['object'] = CreateObjectTypeDefinition()
+type_definitions['any'] = CreateAnyTypeDefinition()
+
+
+def WrapArrayDefinition(type):
+ return {
+ 'return_type': 'std::vector<%s>' % type['type'],
+ 'pass_type': 'std::vector<%s>' % type['type'],
+ 'to_raw_type': '%s',
+ 'to_raw_return_type': '&%s',
+ 'to_pass_type': 'std::move(%s)',
+ 'type': 'std::vector<%s>' % type['type'],
+ 'raw_type': 'std::vector<%s>' % type['type'],
+ 'raw_pass_type': 'std::vector<%s>*' % type['type'],
+ 'raw_return_type': 'const std::vector<%s>*' % type['type'],
+ }
+
+
+def CreateTypeDefinitions(json_api):
+ for domain in json_api['domains']:
+ if not ('types' in domain):
+ continue
+ for type in domain['types']:
+ if type['type'] == 'object':
+ if 'properties' in type:
+ type_definitions[domain['domain'] + '.' + type['id']] = (
+ CreateUserTypeDefinition(domain, type))
+ else:
+ type_definitions[domain['domain'] + '.' + type['id']] = (
+ CreateObjectTypeDefinition())
+ elif type['type'] == 'array':
+ items_type = type['items']['type']
+ type_definitions[domain['domain'] + '.' + type['id']] = (
+ WrapArrayDefinition(type_definitions[items_type]))
+ elif 'enum' in type:
+ type_definitions[domain['domain'] + '.' + type['id']] = (
+ CreateEnumTypeDefinition(domain['domain'], type))
+ type['$ref'] = domain['domain'] + '.' + type['id']
+ elif type['type'] == 'any':
+ type_definitions[domain['domain'] + '.' + type['id']] = (
+ CreateAnyTypeDefinition())
+ else:
+ type_definitions[domain['domain'] + '.' + type['id']] = (
+ CreatePrimitiveTypeDefinition(type['type']))
+
+
+def TypeDefinition(name):
+ return type_definitions[name]
+
+
+def ResolveType(property):
+ if '$ref' in property:
+ return type_definitions[property['$ref']]
+ elif property['type'] == 'object':
+ return WrapObjectTypeDefinition(property)
+ elif property['type'] == 'array':
+ return WrapArrayDefinition(ResolveType(property['items']))
+ return type_definitions[property['type']]
+
+
+def JoinArrays(dict, keys):
+ result = []
+ for key in keys:
+ if key in dict:
+ result += dict[key]
+ return result
+
+
+def SynthesizeEnumType(domain, owner, type):
+ type['id'] = ToTitleCase(owner) + ToTitleCase(type['name'])
+ type_definitions[domain['domain'] + '.' + type['id']] = (
+ CreateEnumTypeDefinition(domain['domain'], type))
+ type['$ref'] = domain['domain'] + '.' + type['id']
+ domain['types'].append(type)
+
+
+def SynthesizeCommandTypes(json_api):
+ """Generate types for command parameters, return values and enum
+ properties."""
+ for domain in json_api['domains']:
+ if not 'types' in domain:
+ domain['types'] = []
+ for type in domain['types']:
+ if type['type'] == 'object':
+ for property in type.get('properties', []):
+ if 'enum' in property and not '$ref' in property:
+ SynthesizeEnumType(domain, type['id'], property)
+
+ for command in domain.get('commands', []):
+ if 'parameters' in command:
+ for parameter in command['parameters']:
+ if 'enum' in parameter and not '$ref' in parameter:
+ SynthesizeEnumType(domain, command['name'], parameter)
+ parameters_type = {
+ 'id': ToTitleCase(command['name']) + 'Params',
+ 'type': 'object',
+ 'description': 'Parameters for the %s command.' % ToTitleCase(
+ command['name']),
+ 'properties': command['parameters']
+ }
+ domain['types'].append(parameters_type)
+ if 'returns' in command:
+ for parameter in command['returns']:
+ if 'enum' in parameter and not '$ref' in parameter:
+ SynthesizeEnumType(domain, command['name'], parameter)
+ result_type = {
+ 'id': ToTitleCase(command['name']) + 'Result',
+ 'type': 'object',
+ 'description': 'Result for the %s command.' % ToTitleCase(
+ command['name']),
+ 'properties': command['returns']
+ }
+ domain['types'].append(result_type)
+
+
+def SynthesizeEventTypes(json_api):
+ """Generate types for events and their properties.
+
+ Note that parameter objects are also created for events without parameters to
+ make it easier to introduce parameters later.
+ """
+ for domain in json_api['domains']:
+ if not 'types' in domain:
+ domain['types'] = []
+ for event in domain.get('events', []):
+ for parameter in event.get('parameters', []):
+ if 'enum' in parameter and not '$ref' in parameter:
+ SynthesizeEnumType(domain, event['name'], parameter)
+ event_type = {
+ 'id': ToTitleCase(event['name']) + 'Params',
+ 'type': 'object',
+ 'description': 'Parameters for the %s event.' % ToTitleCase(
+ event['name']),
+ 'properties': event.get('parameters', [])
+ }
+ domain['types'].append(event_type)
+
+
+def PatchHiddenCommandsAndEvents(json_api):
+ """
+ Mark all commands and events in hidden domains as hidden and make sure hidden
+ commands have at least empty parameters and return values.
+ """
+ for domain in json_api['domains']:
+ if domain.get('hidden', False):
+ for command in domain.get('commands', []):
+ command['hidden'] = True
+ for event in domain.get('events', []):
+ event['hidden'] = True
+ for command in domain.get('commands', []):
+ if not command.get('hidden', False):
+ continue
+ if not 'parameters' in command:
+ command['parameters'] = []
+ if not 'returns' in command:
+ command['returns'] = []
+ for event in domain.get('events', []):
+ if not event.get('hidden', False):
+ continue
+ if not 'parameters' in event:
+ event['parameters'] = []
+
+
+def Generate(jinja_env, output_dirname, json_api, class_name, file_types):
+ template_context = {
+ 'api': json_api,
+ 'join_arrays': JoinArrays,
+ 'resolve_type': ResolveType,
+ 'type_definition': TypeDefinition,
+ }
+ for file_type in file_types:
+ template = jinja_env.get_template('/%s_%s.template' % (
+ class_name, file_type))
+ output_file = '%s/%s.%s' % (output_dirname, class_name, file_type)
+ with open(output_file, 'w') as f:
+ f.write(template.render(template_context))
+
+
+def GenerateDomains(jinja_env, output_dirname, json_api, class_name,
+ file_types):
+ for file_type in file_types:
+ template = jinja_env.get_template('/%s_%s.template' % (
+ class_name, file_type))
+ for domain in json_api['domains']:
+ template_context = {
+ 'domain': domain,
+ 'resolve_type': ResolveType,
+ }
+ domain_name = CamelCaseToHackerStyle(domain['domain'])
+ output_file = '%s/%s.%s' % (output_dirname, domain_name, file_type)
+ with open(output_file, 'w') as f:
+ f.write(template.render(template_context))
+
+
+if __name__ == '__main__':
+ json_api, output_dirname = ParseArguments(sys.argv[1:])
+ jinja_env = InitializeJinjaEnv(output_dirname)
+ PatchHiddenCommandsAndEvents(json_api)
+ SynthesizeCommandTypes(json_api)
+ SynthesizeEventTypes(json_api)
+ PatchFullQualifiedRefs(json_api)
+ CreateTypeDefinitions(json_api)
+ Generate(jinja_env, output_dirname, json_api, 'types', ['cc', 'h'])
+ Generate(jinja_env, output_dirname, json_api, 'type_conversions', ['h'])
+ GenerateDomains(jinja_env, output_dirname, json_api, 'domain', ['cc', 'h'])
diff --git a/chromium/headless/lib/browser/client_api_generator_unittest.py b/chromium/headless/lib/browser/client_api_generator_unittest.py
new file mode 100644
index 00000000000..0cd7df3fd37
--- /dev/null
+++ b/chromium/headless/lib/browser/client_api_generator_unittest.py
@@ -0,0 +1,534 @@
+# 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.
+
+import argparse
+import client_api_generator
+import shutil
+import sys
+import tempfile
+import unittest
+
+
+class ClientApiGeneratorTest(unittest.TestCase):
+
+ def test_ArgumentParsing(self):
+ with tempfile.NamedTemporaryFile() as f:
+ f.write('{"foo": true}')
+ f.flush()
+ json_api, output_dir = client_api_generator.ParseArguments([
+ '--protocol', f.name, '--output_dir', 'out'])
+ self.assertEqual({'foo': True}, json_api)
+ self.assertEqual('out', output_dir)
+
+ def test_ToTitleCase(self):
+ self.assertEqual(client_api_generator.ToTitleCase('fooBar'), 'FooBar')
+
+ def test_DashToCamelCase(self):
+ self.assertEqual(client_api_generator.DashToCamelCase('foo-bar'), 'FooBar')
+ self.assertEqual(client_api_generator.DashToCamelCase('foo-'), 'Foo')
+ self.assertEqual(client_api_generator.DashToCamelCase('-bar'), 'Bar')
+
+ def test_CamelCaseToHackerStyle(self):
+ self.assertEqual(client_api_generator.CamelCaseToHackerStyle('FooBar'),
+ 'foo_bar')
+ self.assertEqual(client_api_generator.CamelCaseToHackerStyle('LoLoLoL'),
+ 'lo_lo_lol')
+
+ def test_MangleEnum(self):
+ self.assertEqual(client_api_generator.MangleEnum('FOO'), 'FOO')
+ self.assertEqual(client_api_generator.MangleEnum('NULL'), 'NONE')
+
+ def test_PatchFullQualifiedRefs(self):
+ json_api = {
+ 'domains': [
+ {
+ 'domain': 'domain0',
+ '$ref': 'reference',
+ },
+ {
+ 'domain': 'domain1',
+ '$ref': 'reference',
+ 'more': [{'$ref': 'domain0.thing'}],
+ }
+ ]
+ }
+ expected_json_api = {
+ 'domains': [
+ {
+ 'domain': 'domain0',
+ '$ref': 'domain0.reference',
+ },
+ {
+ 'domain': 'domain1',
+ '$ref': 'domain1.reference',
+ 'more': [{'$ref': 'domain0.thing'}],
+ }
+ ]
+ }
+ client_api_generator.PatchFullQualifiedRefs(json_api)
+ self.assertDictEqual(json_api, expected_json_api)
+
+ def test_NumberType(self):
+ json_api = {
+ 'domains': [
+ {
+ 'domain': 'domain',
+ 'types': [
+ {
+ 'id': 'TestType',
+ 'type': 'number',
+ },
+ ]
+ },
+ ]
+ }
+ client_api_generator.CreateTypeDefinitions(json_api)
+ type = json_api['domains'][0]['types'][0]
+ resolved = client_api_generator.ResolveType(type)
+ self.assertEqual('double', resolved['raw_type'])
+
+ def test_IntegerType(self):
+ json_api = {
+ 'domains': [
+ {
+ 'domain': 'domain',
+ 'types': [
+ {
+ 'id': 'TestType',
+ 'type': 'integer',
+ },
+ ]
+ },
+ ]
+ }
+ client_api_generator.CreateTypeDefinitions(json_api)
+ type = json_api['domains'][0]['types'][0]
+ resolved = client_api_generator.ResolveType(type)
+ self.assertEqual('int', resolved['raw_type'])
+
+ def test_BooleanType(self):
+ json_api = {
+ 'domains': [
+ {
+ 'domain': 'domain',
+ 'types': [
+ {
+ 'id': 'TestType',
+ 'type': 'boolean',
+ },
+ ]
+ },
+ ]
+ }
+ client_api_generator.CreateTypeDefinitions(json_api)
+ type = json_api['domains'][0]['types'][0]
+ resolved = client_api_generator.ResolveType(type)
+ self.assertEqual('bool', resolved['raw_type'])
+
+ def test_StringType(self):
+ json_api = {
+ 'domains': [
+ {
+ 'domain': 'domain',
+ 'types': [
+ {
+ 'id': 'TestType',
+ 'type': 'string',
+ },
+ ]
+ },
+ ]
+ }
+ client_api_generator.CreateTypeDefinitions(json_api)
+ type = json_api['domains'][0]['types'][0]
+ resolved = client_api_generator.ResolveType(type)
+ self.assertEqual('std::string', resolved['raw_type'])
+
+ def test_ObjectType(self):
+ json_api = {
+ 'domains': [
+ {
+ 'domain': 'domain',
+ 'types': [
+ {
+ 'id': 'TestType',
+ 'type': 'object',
+ 'properties': [
+ {'name': 'p1', 'type': 'number'},
+ {'name': 'p2', 'type': 'integer'},
+ {'name': 'p3', 'type': 'boolean'},
+ {'name': 'p4', 'type': 'string'},
+ {'name': 'p5', 'type': 'any'},
+ {'name': 'p6', 'type': 'object', '$ref': 'TestType'},
+ ],
+ 'returns': [
+ {'name': 'r1', 'type': 'number'},
+ {'name': 'r2', 'type': 'integer'},
+ {'name': 'r3', 'type': 'boolean'},
+ {'name': 'r4', 'type': 'string'},
+ {'name': 'r5', 'type': 'any'},
+ {'name': 'r6', 'type': 'object', '$ref': 'TestType'},
+ ],
+ },
+ ]
+ },
+ ]
+ }
+ client_api_generator.CreateTypeDefinitions(json_api)
+ type = json_api['domains'][0]['types'][0]
+ resolved = client_api_generator.ResolveType(type)
+ self.assertEqual('TestType', resolved['raw_type'])
+
+ def test_AnyType(self):
+ json_api = {
+ 'domains': [
+ {
+ 'domain': 'domain',
+ 'types': [
+ {
+ 'id': 'TestType',
+ 'type': 'any',
+ },
+ ]
+ },
+ ]
+ }
+ client_api_generator.CreateTypeDefinitions(json_api)
+ type = json_api['domains'][0]['types'][0]
+ resolved = client_api_generator.ResolveType(type)
+ self.assertEqual('base::Value', resolved['raw_type'])
+
+ def test_ArrayType(self):
+ json_api = {
+ 'domains': [
+ {
+ 'domain': 'domain',
+ 'types': [
+ {
+ 'id': 'TestType',
+ 'type': 'array',
+ 'items': {'type': 'integer'}
+ },
+ ]
+ },
+ ]
+ }
+ client_api_generator.CreateTypeDefinitions(json_api)
+ type = json_api['domains'][0]['types'][0]
+ resolved = client_api_generator.ResolveType(type)
+ self.assertEqual('std::vector<int>', resolved['raw_type'])
+
+ def test_EnumType(self):
+ json_api = {
+ 'domains': [
+ {
+ 'domain': 'domain',
+ 'types': [
+ {
+ 'id': 'TestType',
+ 'type': 'string',
+ 'enum': ['a', 'b', 'c']
+ },
+ ]
+ },
+ ]
+ }
+ client_api_generator.CreateTypeDefinitions(json_api)
+ type = json_api['domains'][0]['types'][0]
+ resolved = client_api_generator.ResolveType(type)
+ self.assertEqual('headless::domain::TestType', resolved['raw_type'])
+
+ def test_SynthesizeCommandTypes(self):
+ json_api = {
+ 'domains': [
+ {
+ 'domain': 'domain',
+ 'commands': [
+ {
+ 'name': 'TestCommand',
+ 'parameters': [
+ {'name': 'p1', 'type': 'number'},
+ {'name': 'p2', 'type': 'integer'},
+ {'name': 'p3', 'type': 'boolean'},
+ {'name': 'p4', 'type': 'string'},
+ {'name': 'p5', 'type': 'any'},
+ {'name': 'p6', 'type': 'object', '$ref': 'TestType'},
+ ],
+ 'returns': [
+ {'name': 'r1', 'type': 'number'},
+ {'name': 'r2', 'type': 'integer'},
+ {'name': 'r3', 'type': 'boolean'},
+ {'name': 'r4', 'type': 'string'},
+ {'name': 'r5', 'type': 'any'},
+ {'name': 'r6', 'type': 'object', '$ref': 'TestType'},
+ ],
+ },
+ ]
+ },
+ ]
+ }
+ expected_types = [
+ {
+ 'type': 'object',
+ 'id': 'TestCommandParams',
+ 'description': 'Parameters for the TestCommand command.',
+ 'properties': [
+ {'type': 'number', 'name': 'p1'},
+ {'type': 'integer', 'name': 'p2'},
+ {'type': 'boolean', 'name': 'p3'},
+ {'type': 'string', 'name': 'p4'},
+ {'type': 'any', 'name': 'p5'},
+ {'type': 'object', 'name': 'p6', '$ref': 'TestType'}
+ ],
+ },
+ {
+ 'type': 'object',
+ 'id': 'TestCommandResult',
+ 'description': 'Result for the TestCommand command.',
+ 'properties': [
+ {'type': 'number', 'name': 'r1'},
+ {'type': 'integer', 'name': 'r2'},
+ {'type': 'boolean', 'name': 'r3'},
+ {'type': 'string', 'name': 'r4'},
+ {'type': 'any', 'name': 'r5'},
+ {'type': 'object', 'name': 'r6', '$ref': 'TestType'}
+ ],
+ }
+ ]
+ client_api_generator.SynthesizeCommandTypes(json_api)
+ types = json_api['domains'][0]['types']
+ self.assertListEqual(types, expected_types)
+
+ def test_SynthesizeEventTypes(self):
+ json_api = {
+ 'domains': [
+ {
+ 'domain': 'domain',
+ 'events': [
+ {
+ 'name': 'TestEvent',
+ 'parameters': [
+ {'name': 'p1', 'type': 'number'},
+ {'name': 'p2', 'type': 'integer'},
+ {'name': 'p3', 'type': 'boolean'},
+ {'name': 'p4', 'type': 'string'},
+ {'name': 'p5', 'type': 'any'},
+ {'name': 'p6', 'type': 'object', '$ref': 'TestType'},
+ ]
+ },
+ {
+ 'name': 'TestEventWithNoParams',
+ }
+ ]
+ }
+ ]
+ }
+ expected_types = [
+ {
+ 'type': 'object',
+ 'id': 'TestEventParams',
+ 'description': 'Parameters for the TestEvent event.',
+ 'properties': [
+ {'type': 'number', 'name': 'p1'},
+ {'type': 'integer', 'name': 'p2'},
+ {'type': 'boolean', 'name': 'p3'},
+ {'type': 'string', 'name': 'p4'},
+ {'type': 'any', 'name': 'p5'},
+ {'type': 'object', 'name': 'p6', '$ref': 'TestType'}
+ ]
+ },
+ {
+ 'type': 'object',
+ 'id': 'TestEventWithNoParamsParams',
+ 'description': 'Parameters for the TestEventWithNoParams event.',
+ 'properties': [],
+ }
+ ]
+ client_api_generator.SynthesizeEventTypes(json_api)
+ types = json_api['domains'][0]['types']
+ self.assertListEqual(types, expected_types)
+
+ def test_PatchHiddenDomains(self):
+ json_api = {
+ 'domains': [
+ {
+ 'domain': 'domain',
+ 'hidden': True,
+ 'commands': [
+ {
+ 'name': 'FooCommand',
+ }
+ ],
+ 'events': [
+ {
+ 'name': 'BarEvent',
+ }
+ ]
+ }
+ ]
+ }
+ expected_types = [
+ {
+ 'type': 'object',
+ 'id': 'FooCommandParams',
+ 'description': 'Parameters for the FooCommand command.',
+ 'properties': [],
+ },
+ {
+ 'type': 'object',
+ 'id': 'FooCommandResult',
+ 'description': 'Result for the FooCommand command.',
+ 'properties': [],
+ },
+ {
+ 'type': 'object',
+ 'id': 'BarEventParams',
+ 'description': 'Parameters for the BarEvent event.',
+ 'properties': [],
+ }
+ ]
+ client_api_generator.PatchHiddenCommandsAndEvents(json_api)
+ client_api_generator.SynthesizeCommandTypes(json_api)
+ client_api_generator.SynthesizeEventTypes(json_api)
+ for command in json_api['domains'][0]['commands']:
+ self.assertTrue(command['hidden'])
+ for event in json_api['domains'][0]['events']:
+ self.assertTrue(command['hidden'])
+ types = json_api['domains'][0]['types']
+ self.assertListEqual(types, expected_types)
+
+ def test_PatchHiddenCommandsAndEvents(self):
+ json_api = {
+ 'domains': [
+ {
+ 'domain': 'domain',
+ 'commands': [
+ {
+ 'name': 'FooCommand',
+ 'hidden': True,
+ }
+ ],
+ 'events': [
+ {
+ 'name': 'BarEvent',
+ 'hidden': True,
+ }
+ ]
+ }
+ ]
+ }
+ expected_types = [
+ {
+ 'type': 'object',
+ 'id': 'FooCommandParams',
+ 'description': 'Parameters for the FooCommand command.',
+ 'properties': [],
+ },
+ {
+ 'type': 'object',
+ 'id': 'FooCommandResult',
+ 'description': 'Result for the FooCommand command.',
+ 'properties': [],
+ },
+ {
+ 'type': 'object',
+ 'id': 'BarEventParams',
+ 'description': 'Parameters for the BarEvent event.',
+ 'properties': [],
+ }
+ ]
+ client_api_generator.PatchHiddenCommandsAndEvents(json_api)
+ client_api_generator.SynthesizeCommandTypes(json_api)
+ client_api_generator.SynthesizeEventTypes(json_api)
+ for command in json_api['domains'][0]['commands']:
+ self.assertTrue(command['hidden'])
+ for event in json_api['domains'][0]['events']:
+ self.assertTrue(command['hidden'])
+ types = json_api['domains'][0]['types']
+ self.assertListEqual(types, expected_types)
+
+ def test_Generate(self):
+ json_api = {
+ 'domains': [
+ {
+ 'domain': 'domain',
+ 'types': [
+ {
+ 'id': 'TestType',
+ 'type': 'object',
+ 'properties': [
+ {'name': 'p1', 'type': 'number'},
+ {'name': 'p2', 'type': 'integer'},
+ {'name': 'p3', 'type': 'boolean'},
+ {'name': 'p4', 'type': 'string'},
+ {'name': 'p5', 'type': 'any'},
+ {'name': 'p6', 'type': 'object', '$ref': 'domain.TestType'},
+ ],
+ 'returns': [
+ {'name': 'r1', 'type': 'number'},
+ {'name': 'r2', 'type': 'integer'},
+ {'name': 'r3', 'type': 'boolean'},
+ {'name': 'r4', 'type': 'string'},
+ {'name': 'r5', 'type': 'any'},
+ {'name': 'r6', 'type': 'object', '$ref': 'domain.TestType'},
+ ],
+ },
+ ]
+ },
+ ]
+ }
+ try:
+ dirname = tempfile.mkdtemp()
+ jinja_env = client_api_generator.InitializeJinjaEnv(dirname)
+ client_api_generator.Generate(jinja_env, dirname, json_api, 'types',
+ ['cc'])
+ client_api_generator.Generate(jinja_env, dirname, json_api, 'types',
+ ['h'])
+ # This is just a smoke test; we don't actually verify the generated output
+ # here.
+ finally:
+ shutil.rmtree(dirname)
+
+ def test_GenerateDomains(self):
+ json_api = {
+ 'domains': [
+ {
+ 'domain': 'domain0',
+ 'types': [
+ {
+ 'id': 'TestType',
+ 'type': 'object',
+ },
+ ]
+ },
+ {
+ 'domain': 'domain1',
+ 'types': [
+ {
+ 'id': 'TestType',
+ 'type': 'object',
+ },
+ ]
+ },
+ ]
+ }
+ try:
+ dirname = tempfile.mkdtemp()
+ jinja_env = client_api_generator.InitializeJinjaEnv(dirname)
+ client_api_generator.GenerateDomains(jinja_env, dirname, json_api,
+ 'domain', ['cc', 'h'])
+ # This is just a smoke test; we don't actually verify the generated output
+ # here.
+ finally:
+ shutil.rmtree(dirname)
+
+
+if __name__ == '__main__':
+ cmdline_parser = argparse.ArgumentParser()
+ cmdline_parser.add_argument('--stamp')
+ args = cmdline_parser.parse_args()
+ unittest.main(verbosity=2, exit=False, argv=sys.argv[:1])
+ if args.stamp:
+ with open(args.stamp, 'a') as f:
+ pass
diff --git a/chromium/headless/lib/browser/domain_cc.template b/chromium/headless/lib/browser/domain_cc.template
new file mode 100644
index 00000000000..118d996627e
--- /dev/null
+++ b/chromium/headless/lib/browser/domain_cc.template
@@ -0,0 +1,142 @@
+// This file is generated
+
+// 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/public/domains/{{domain.domain | camelcase_to_hacker_style}}.h"
+
+#include "base/bind.h"
+
+namespace headless {
+
+namespace {{domain.domain | camelcase_to_hacker_style}} {
+
+ExperimentalDomain* Domain::GetExperimental() {
+ return static_cast<ExperimentalDomain*>(this);
+}
+
+ {% if "events" in domain %}
+void Domain::AddObserver(Observer* observer) {
+ observers_.AddObserver(observer);
+}
+
+void Domain::RemoveObserver(Observer* observer) {
+ observers_.RemoveObserver(observer);
+}
+ {% endif %}
+
+ {% for command in domain.commands %}
+ {# Skip redirected commands. #}
+ {% if "redirect" in command %}{% continue %}{% endif %}
+
+ {% set class_name = 'ExperimentalDomain' if command.hidden else 'Domain' %}
+ {% set method_name = command.name | to_title_case %}
+ {% if "parameters" in command and "returns" in command %}
+void {{class_name}}::{{method_name}}(std::unique_ptr<{{method_name}}Params> params, base::Callback<void(std::unique_ptr<{{method_name}}Result>)> callback) {
+ dispatcher_->SendMessage("{{domain.domain}}.{{command.name}}", params->Serialize(), base::Bind(&Domain::Handle{{method_name}}Response, callback));
+ {% elif "parameters" in command %}
+void {{class_name}}::{{method_name}}(std::unique_ptr<{{method_name}}Params> params, base::Callback<void()> callback) {
+ dispatcher_->SendMessage("{{domain.domain}}.{{command.name}}", params->Serialize(), std::move(callback));
+ {% elif "returns" in command %}
+void {{class_name}}::{{method_name}}(base::Callback<void(std::unique_ptr<{{method_name}}Result>)> callback) {
+ dispatcher_->SendMessage("{{domain.domain}}.{{command.name}}", base::Bind(&Domain::Handle{{method_name}}Response, std::move(callback)));
+ {% else %}
+void {{class_name}}::{{method_name}}(base::Callback<void()> callback) {
+ dispatcher_->SendMessage("{{domain.domain}}.{{command.name}}", std::move(callback));
+ {% endif %}
+}
+ {# Generate convenience methods that take the required parameters directly. #}
+ {% if not "parameters" in command %}{% continue %}{% endif %}
+ {# Don't generate these for hidden commands. #}
+ {% if command.hidden %}{% continue %}{% endif %}
+
+void {{class_name}}::{{method_name}}({##}
+ {% for parameter in command.parameters -%}
+ {% if parameter.get("optional", False) -%}
+ {% break %}
+ {% endif %}
+ {% if not loop.first %}, {% endif %}
+{{resolve_type(parameter).pass_type}} {{parameter.name | camelcase_to_hacker_style -}}
+ {% endfor %}
+ {% if "parameters" in command and not command.parameters[0].get("optional", False) %}, {% endif %}{# -#}
+ {% if "returns" in command -%}
+ base::Callback<void(std::unique_ptr<{{method_name}}Result>)> callback{##}
+ {% else -%}
+ base::Callback<void()> callback{##}
+ {% endif %}) {
+ {# Build the parameters object. #}
+ std::unique_ptr<{{method_name}}Params> params = {{method_name}}Params::Builder()
+ {% for parameter in command.parameters -%}
+ {% if parameter.get("optional", False) -%}
+ {% break %}
+ {% endif %}
+ .Set{{parameter.name | to_title_case}}(std::move({{parameter.name | camelcase_to_hacker_style}}))
+ {% endfor %}
+ .Build();
+ {# Send the message. #}
+ {{method_name}}(std::move(params), std::move(callback));
+}
+ {% endfor %}
+
+{# Generate response handlers for commands that need them. #}
+{% for command in domain.commands %}
+ {% if not "returns" in command %}{% continue %}{% endif %}
+ {% set method_name = command.name | to_title_case %}
+
+// static
+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;
+ ErrorReporter errors;
+ std::unique_ptr<{{method_name}}Result> result = {{method_name}}Result::Parse(response, &errors);
+ DCHECK(!errors.HasErrors());
+ callback.Run(std::move(result));
+}
+{% endfor %}
+{% if "events" in domain %}
+ {% for event in domain.events %}
+
+{# Generate dispatchers which call registered observers for an event. #}
+void Domain::Dispatch{{event.name | to_title_case}}Event(const base::Value& params) {
+ ErrorReporter errors;
+ std::unique_ptr<{{event.name | to_title_case}}Params> parsed_params({{event.name | to_title_case}}Params::Parse(params, &errors));
+ DCHECK(!errors.HasErrors());
+ FOR_EACH_OBSERVER(ExperimentalObserver, observers_, On{{event.name | to_title_case}}(*parsed_params));
+}
+ {% endfor %}
+{% endif %}
+
+Domain::Domain(internal::MessageDispatcher* dispatcher)
+ : dispatcher_(dispatcher) {
+ {% if "events" in domain %}
+ {# Register all events in this domain. #}
+ {% for event in domain.events %}
+ dispatcher_->RegisterEventHandler(
+ "{{domain.domain}}.{{event.name}}",
+ base::Bind(&Domain::Dispatch{{event.name | to_title_case}}Event,
+ base::Unretained(this)));
+ {% endfor %}
+ {% endif %}
+}
+
+Domain::~Domain() {}
+
+ExperimentalDomain::ExperimentalDomain(internal::MessageDispatcher* dispatcher)
+ : Domain(dispatcher) {}
+
+ExperimentalDomain::~ExperimentalDomain() {}
+
+ {% if "events" in domain %}
+void ExperimentalDomain::AddObserver(ExperimentalObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void ExperimentalDomain::RemoveObserver(ExperimentalObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+ {% endif %}
+
+} // namespace {{domain.domain | camelcase_to_hacker_style}}
+
+} // namespace headless
diff --git a/chromium/headless/lib/browser/domain_h.template b/chromium/headless/lib/browser/domain_h.template
new file mode 100644
index 00000000000..607b5be16f2
--- /dev/null
+++ b/chromium/headless/lib/browser/domain_h.template
@@ -0,0 +1,158 @@
+// This file is generated
+
+// Copyright (c) 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_PUBLIC_DOMAINS_{{domain.domain | camelcase_to_hacker_style | upper}}_H_
+#define HEADLESS_PUBLIC_DOMAINS_{{domain.domain | camelcase_to_hacker_style | upper}}_H_
+
+#include "base/callback.h"
+#include "base/observer_list.h"
+#include "base/values.h"
+#include "headless/public/domains/types.h"
+#include "headless/public/headless_export.h"
+#include "headless/public/internal/message_dispatcher.h"
+
+{# Macro for defining a member function for a given command. #}
+{% macro command_decl(command) %}
+ {% set method_name = command.name | to_title_case %}
+ {% if command.description %}
+ // {{ command.description }}
+ {% endif %}
+ {% if "parameters" in command and "returns" in command %}
+ void {{method_name}}(std::unique_ptr<{{method_name}}Params> params, base::Callback<void(std::unique_ptr<{{method_name}}Result>)> callback = base::Callback<void(std::unique_ptr<{{method_name}}Result>)>());
+ {% elif "parameters" in command %}
+ void {{method_name}}(std::unique_ptr<{{method_name}}Params> params, base::Callback<void()> callback = base::Callback<void()>());
+ {% elif "returns" in command %}
+ void {{method_name}}(base::Callback<void(std::unique_ptr<{{method_name}}Result>)> callback = base::Callback<void(std::unique_ptr<{{method_name}}Result>)>());
+ {% else %}
+ void {{method_name}}(base::Callback<void()> callback = base::Callback<void()>());
+ {% endif %}
+ {# Generate convenience methods that take the required parameters directly. #}
+ {# Don't generate these for hidden commands. #}
+ {% if "parameters" in command and not command.hidden %}
+ void {{method_name}}({##}
+ {% for parameter in command.parameters -%}
+ {% if parameter.get("optional", False) -%}
+ {% break %}
+ {% endif %}
+ {% if not loop.first %}, {% endif %}
+{{resolve_type(parameter).pass_type}} {{parameter.name | camelcase_to_hacker_style -}}
+ {% endfor %}
+ {% if "parameters" in command and not command.parameters[0].get("optional", False) %}, {% endif %}{# -#}
+ {% if "returns" in command -%}
+ base::Callback<void(std::unique_ptr<{{method_name}}Result>)> callback = base::Callback<void(std::unique_ptr<{{method_name}}Result>)>(){##}
+ {% else -%}
+ base::Callback<void()> callback = base::Callback<void()>(){##}
+ {% endif %});
+ {% endif %}
+{% endmacro %}
+
+namespace headless {
+namespace {{domain.domain | camelcase_to_hacker_style}} {
+class ExperimentalDomain;
+class ExperimentalObserver;
+{% if "events" in domain %}
+
+class HEADLESS_EXPORT ExperimentalObserver {
+ public:
+ virtual ~ExperimentalObserver() {}
+ {% for event in domain.events %}
+ {% if event.description %}
+ // {{event.description}}
+ {% endif %}
+ virtual void On{{event.name | to_title_case}}(const {{event.name | to_title_case}}Params& params) {}
+ {% endfor %}
+};
+
+class HEADLESS_EXPORT Observer : public ExperimentalObserver {
+ public:
+ virtual ~Observer() {}
+ {% for event in domain.events %}
+ {% if event.description %}
+ // {% if event.hidden %}Experimental: {% endif %}{{event.description}}
+ {% endif %}
+ virtual void On{{event.name | to_title_case}}(const {{event.name | to_title_case}}Params& params) {% if event.hidden %}final {% endif %}{}
+ {% endfor %}
+};
+{% endif %}
+
+{% if domain.description %}
+// {{domain.description}}
+{% endif %}
+class HEADLESS_EXPORT Domain {
+ public:
+ {% if "events" in domain %}
+ // Add or remove an observer. |observer| must be removed before being
+ // destroyed.
+ void AddObserver(Observer* observer);
+ void RemoveObserver(Observer* observer);
+ {% endif %}
+
+ // Return the experimental interface for this domain. Note that experimental
+ // commands may be changed or removed at any time.
+ ExperimentalDomain* GetExperimental();
+
+ {# Generate methods for each command. #}
+ {% for command in domain.commands %}
+ {# Skip redirected commands. #}
+ {% if "redirect" in command %}{% continue %}{% endif %}
+ {# Skip hidden commands. #}
+ {% if command.hidden %}{% continue %}{% endif %}
+{{ command_decl(command) }}
+ {% endfor %}
+ protected:
+ Domain(internal::MessageDispatcher* dispatcher);
+ ~Domain();
+
+ {# Generate response handlers for commands that need them. #}
+ {% for command in domain.commands %}
+ {% if not "returns" in command %}{% continue %}{% endif %}
+ {% set method_name = command.name | to_title_case %}
+ static void Handle{{method_name}}Response(base::Callback<void(std::unique_ptr<{{method_name}}Result>)> callback, const base::Value& response);
+ {% endfor %}
+
+ {# Generate event dispatchers. #}
+ {% for event in domain.events %}
+ void Dispatch{{event.name | to_title_case}}Event(const base::Value& params);
+ {% endfor %}
+
+ internal::MessageDispatcher* dispatcher_; // Not owned.
+ {% if "events" in domain %}
+ base::ObserverList<ExperimentalObserver> observers_;
+ {% endif %}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Domain);
+};
+
+class ExperimentalDomain : public Domain {
+ public:
+ ExperimentalDomain(internal::MessageDispatcher* dispatcher);
+ ~ExperimentalDomain();
+
+ {% if "events" in domain %}
+ // Add or remove an observer. |observer| must be removed before being
+ // destroyed.
+ void AddObserver(ExperimentalObserver* observer);
+ void RemoveObserver(ExperimentalObserver* observer);
+ {% endif %}
+
+ {# Generate methods for each experimental command. #}
+ {% for command in domain.commands %}
+ {# Skip redirected commands. #}
+ {% if "redirect" in command %}{% continue %}{% endif %}
+ {# Skip non-hidden commands. #}
+ {% if not command.hidden %}{% continue %}{% endif %}
+{{ command_decl(command) }}
+ {% endfor %}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ExperimentalDomain);
+};
+
+} // namespace {{domain.domain | camelcase_to_hacker_style}}
+} // namespace headless
+
+#endif // HEADLESS_PUBLIC_DOMAINS_{{domain.domain | camelcase_to_hacker_style | upper}}_H_
diff --git a/chromium/headless/lib/browser/headless_browser_context.cc b/chromium/headless/lib/browser/headless_browser_context.cc
index b48f09012e2..59e50ae2025 100644
--- a/chromium/headless/lib/browser/headless_browser_context.cc
+++ b/chromium/headless/lib/browser/headless_browser_context.cc
@@ -36,6 +36,10 @@ class HeadlessResourceContext : public content::ResourceContext {
url_request_context_getter_ = std::move(url_request_context_getter);
}
+ net::URLRequestContextGetter* url_request_context_getter() {
+ return url_request_context_getter_.get();
+ }
+
private:
scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
@@ -93,27 +97,6 @@ bool HeadlessBrowserContext::IsOffTheRecord() const {
return false;
}
-net::URLRequestContextGetter* HeadlessBrowserContext::GetRequestContext() {
- return GetDefaultStoragePartition(this)->GetURLRequestContext();
-}
-
-net::URLRequestContextGetter* HeadlessBrowserContext::GetMediaRequestContext() {
- return GetRequestContext();
-}
-
-net::URLRequestContextGetter*
-HeadlessBrowserContext::GetMediaRequestContextForRenderProcess(
- int renderer_child_id) {
- return GetRequestContext();
-}
-
-net::URLRequestContextGetter*
-HeadlessBrowserContext::GetMediaRequestContextForStoragePartition(
- const base::FilePath& partition_path,
- bool in_memory) {
- return GetRequestContext();
-}
-
content::ResourceContext* HeadlessBrowserContext::GetResourceContext() {
return resource_context_.get();
}
@@ -177,6 +160,18 @@ HeadlessBrowserContext::CreateRequestContextForStoragePartition(
return nullptr;
}
+net::URLRequestContextGetter*
+HeadlessBrowserContext::CreateMediaRequestContext() {
+ return resource_context_->url_request_context_getter();
+}
+
+net::URLRequestContextGetter*
+HeadlessBrowserContext::CreateMediaRequestContextForStoragePartition(
+ const base::FilePath& partition_path,
+ bool in_memory) {
+ return nullptr;
+}
+
void HeadlessBrowserContext::SetOptionsForTesting(
const HeadlessBrowser::Options& options) {
options_ = options;
diff --git a/chromium/headless/lib/browser/headless_browser_context.h b/chromium/headless/lib/browser/headless_browser_context.h
index 6fd6676de16..865b3c7f977 100644
--- a/chromium/headless/lib/browser/headless_browser_context.h
+++ b/chromium/headless/lib/browser/headless_browser_context.h
@@ -27,13 +27,6 @@ class HeadlessBrowserContext : public content::BrowserContext {
const base::FilePath& partition_path) override;
base::FilePath GetPath() const override;
bool IsOffTheRecord() const override;
- net::URLRequestContextGetter* GetRequestContext() override;
- net::URLRequestContextGetter* GetMediaRequestContext() override;
- net::URLRequestContextGetter* GetMediaRequestContextForRenderProcess(
- int renderer_child_id) override;
- net::URLRequestContextGetter* GetMediaRequestContextForStoragePartition(
- const base::FilePath& partition_path,
- bool in_memory) override;
content::ResourceContext* GetResourceContext() override;
content::DownloadManagerDelegate* GetDownloadManagerDelegate() override;
content::BrowserPluginGuestManager* GetGuestManager() override;
@@ -50,6 +43,10 @@ class HeadlessBrowserContext : public content::BrowserContext {
bool in_memory,
content::ProtocolHandlerMap* protocol_handlers,
content::URLRequestInterceptorScopedVector request_interceptors) override;
+ net::URLRequestContextGetter* CreateMediaRequestContext() override;
+ net::URLRequestContextGetter* CreateMediaRequestContextForStoragePartition(
+ const base::FilePath& partition_path,
+ bool in_memory) override;
const HeadlessBrowser::Options& options() const { return options_; }
void SetOptionsForTesting(const HeadlessBrowser::Options& options);
diff --git a/chromium/headless/lib/browser/headless_browser_impl.cc b/chromium/headless/lib/browser/headless_browser_impl.cc
index 57564d7ddc8..06b3f14ad80 100644
--- a/chromium/headless/lib/browser/headless_browser_impl.cc
+++ b/chromium/headless/lib/browser/headless_browser_impl.cc
@@ -5,13 +5,14 @@
#include "headless/lib/browser/headless_browser_impl.h"
#include "base/memory/ptr_util.h"
-#include "base/thread_task_runner_handle.h"
+#include "base/threading/thread_task_runner_handle.h"
#include "content/public/app/content_main.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_contents.h"
#include "headless/lib/browser/headless_browser_context.h"
#include "headless/lib/browser/headless_browser_main_parts.h"
#include "headless/lib/browser/headless_web_contents_impl.h"
+#include "headless/lib/browser/headless_window_tree_client.h"
#include "headless/lib/headless_content_main_delegate.h"
#include "ui/aura/env.h"
#include "ui/aura/window_tree_host.h"
@@ -30,19 +31,18 @@ HeadlessBrowserImpl::HeadlessBrowserImpl(
HeadlessBrowserImpl::~HeadlessBrowserImpl() {}
-std::unique_ptr<HeadlessWebContents> HeadlessBrowserImpl::CreateWebContents(
+HeadlessWebContents* HeadlessBrowserImpl::CreateWebContents(
const GURL& initial_url,
const gfx::Size& size) {
DCHECK(BrowserMainThread()->BelongsToCurrentThread());
- std::unique_ptr<HeadlessWebContentsImpl> web_contents =
- base::WrapUnique(new HeadlessWebContentsImpl(
- browser_context(), window_tree_host_->window(), size));
- // We require the user to pass in an initial URL to ensure that the renderer
- // gets initialized and eventually becomes ready to be inspected. See
- // HeadlessWebContents::Observer::WebContentsReady.
- if (!web_contents->OpenURL(initial_url))
+ std::unique_ptr<HeadlessWebContentsImpl> headless_web_contents =
+ HeadlessWebContentsImpl::Create(browser_context(),
+ window_tree_host_->window(), size, this);
+
+ if (!headless_web_contents->OpenURL(initial_url))
return nullptr;
- return std::move(web_contents);
+
+ return RegisterWebContents(std::move(headless_web_contents));
}
scoped_refptr<base::SingleThreadTaskRunner>
@@ -57,6 +57,17 @@ void HeadlessBrowserImpl::Shutdown() {
base::MessageLoop::QuitWhenIdleClosure());
}
+std::vector<HeadlessWebContents*> HeadlessBrowserImpl::GetAllWebContents() {
+ std::vector<HeadlessWebContents*> result;
+ result.reserve(web_contents_.size());
+
+ for (const auto& web_contents_pair : web_contents_) {
+ result.push_back(web_contents_pair.first);
+ }
+
+ return result;
+}
+
HeadlessBrowserContext* HeadlessBrowserImpl::browser_context() const {
DCHECK(BrowserMainThread()->BelongsToCurrentThread());
DCHECK(browser_main_parts());
@@ -79,10 +90,27 @@ void HeadlessBrowserImpl::RunOnStartCallback() {
window_tree_host_.reset(aura::WindowTreeHost::Create(gfx::Rect()));
window_tree_host_->InitHost();
+ window_tree_client_.reset(
+ new HeadlessWindowTreeClient(window_tree_host_->window()));
+
on_start_callback_.Run(this);
on_start_callback_ = base::Callback<void(HeadlessBrowser*)>();
}
+HeadlessWebContentsImpl* HeadlessBrowserImpl::RegisterWebContents(
+ std::unique_ptr<HeadlessWebContentsImpl> web_contents) {
+ HeadlessWebContentsImpl* unowned_web_contents = web_contents.get();
+ web_contents_[unowned_web_contents] = std::move(web_contents);
+ return unowned_web_contents;
+}
+
+void HeadlessBrowserImpl::DestroyWebContents(
+ HeadlessWebContentsImpl* web_contents) {
+ auto it = web_contents_.find(web_contents);
+ DCHECK(it != web_contents_.end());
+ web_contents_.erase(it);
+}
+
void HeadlessBrowserImpl::SetOptionsForTesting(
const HeadlessBrowser::Options& options) {
options_ = options;
diff --git a/chromium/headless/lib/browser/headless_browser_impl.h b/chromium/headless/lib/browser/headless_browser_impl.h
index eadeb56b64d..53bcf1385ac 100644
--- a/chromium/headless/lib/browser/headless_browser_impl.h
+++ b/chromium/headless/lib/browser/headless_browser_impl.h
@@ -8,12 +8,17 @@
#include "headless/public/headless_browser.h"
#include <memory>
+#include <unordered_map>
#include "base/synchronization/lock.h"
#include "headless/lib/browser/headless_web_contents_impl.h"
namespace aura {
class WindowTreeHost;
+
+namespace client {
+class WindowTreeClient;
+}
}
namespace headless {
@@ -29,14 +34,15 @@ class HeadlessBrowserImpl : public HeadlessBrowser {
~HeadlessBrowserImpl() override;
// HeadlessBrowser implementation:
- std::unique_ptr<HeadlessWebContents> CreateWebContents(
- const GURL& initial_url,
- const gfx::Size& size) override;
+ HeadlessWebContents* CreateWebContents(const GURL& initial_url,
+ const gfx::Size& size) override;
scoped_refptr<base::SingleThreadTaskRunner> BrowserMainThread()
const override;
void Shutdown() override;
+ std::vector<HeadlessWebContents*> GetAllWebContents() override;
+
void set_browser_main_parts(HeadlessBrowserMainParts* browser_main_parts);
HeadlessBrowserMainParts* browser_main_parts() const;
@@ -46,6 +52,12 @@ class HeadlessBrowserImpl : public HeadlessBrowser {
const HeadlessBrowser::Options& options() const { return options_; }
+ HeadlessWebContentsImpl* RegisterWebContents(
+ std::unique_ptr<HeadlessWebContentsImpl> web_contents);
+
+ // Close given |web_contents| and delete it.
+ void DestroyWebContents(HeadlessWebContentsImpl* web_contents);
+
// Customize the options used by this headless browser instance. Note that
// options which take effect before the message loop has been started (e.g.,
// custom message pumps) cannot be set via this method.
@@ -56,6 +68,10 @@ class HeadlessBrowserImpl : public HeadlessBrowser {
HeadlessBrowser::Options options_;
HeadlessBrowserMainParts* browser_main_parts_; // Not owned.
std::unique_ptr<aura::WindowTreeHost> window_tree_host_;
+ std::unique_ptr<aura::client::WindowTreeClient> window_tree_client_;
+
+ std::unordered_map<HeadlessWebContents*, std::unique_ptr<HeadlessWebContents>>
+ web_contents_;
DISALLOW_COPY_AND_ASSIGN(HeadlessBrowserImpl);
};
diff --git a/chromium/headless/lib/browser/headless_browser_main_parts.cc b/chromium/headless/lib/browser/headless_browser_main_parts.cc
index c85ba353244..7a2ed47f028 100644
--- a/chromium/headless/lib/browser/headless_browser_main_parts.cc
+++ b/chromium/headless/lib/browser/headless_browser_main_parts.cc
@@ -10,7 +10,7 @@
#include "headless/lib/browser/headless_devtools.h"
#include "headless/lib/browser/headless_screen.h"
#include "ui/aura/env.h"
-#include "ui/gfx/screen.h"
+#include "ui/display/screen.h"
namespace headless {
@@ -18,11 +18,10 @@ namespace {
void PlatformInitialize() {
HeadlessScreen* screen = HeadlessScreen::Create(gfx::Size());
- gfx::Screen::SetScreenInstance(screen);
+ display::Screen::SetScreenInstance(screen);
}
void PlatformExit() {
- aura::Env::DeleteInstance();
}
} // namespace
diff --git a/chromium/headless/lib/browser/headless_devtools_client_impl.cc b/chromium/headless/lib/browser/headless_devtools_client_impl.cc
new file mode 100644
index 00000000000..2f662bb6695
--- /dev/null
+++ b/chromium/headless/lib/browser/headless_devtools_client_impl.cc
@@ -0,0 +1,346 @@
+// 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_devtools_client_impl.h"
+
+#include <memory>
+
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+#include "content/public/browser/devtools_agent_host.h"
+
+namespace headless {
+
+// static
+std::unique_ptr<HeadlessDevToolsClient> HeadlessDevToolsClient::Create() {
+ return base::WrapUnique(new HeadlessDevToolsClientImpl());
+}
+
+// static
+HeadlessDevToolsClientImpl* HeadlessDevToolsClientImpl::From(
+ HeadlessDevToolsClient* client) {
+ // This downcast is safe because there is only one implementation of
+ // HeadlessDevToolsClient.
+ return static_cast<HeadlessDevToolsClientImpl*>(client);
+}
+
+HeadlessDevToolsClientImpl::HeadlessDevToolsClientImpl()
+ : agent_host_(nullptr),
+ next_message_id_(0),
+ accessibility_domain_(this),
+ animation_domain_(this),
+ application_cache_domain_(this),
+ cache_storage_domain_(this),
+ console_domain_(this),
+ css_domain_(this),
+ database_domain_(this),
+ debugger_domain_(this),
+ device_orientation_domain_(this),
+ dom_debugger_domain_(this),
+ dom_domain_(this),
+ dom_storage_domain_(this),
+ emulation_domain_(this),
+ heap_profiler_domain_(this),
+ indexeddb_domain_(this),
+ input_domain_(this),
+ inspector_domain_(this),
+ io_domain_(this),
+ layer_tree_domain_(this),
+ memory_domain_(this),
+ network_domain_(this),
+ page_domain_(this),
+ profiler_domain_(this),
+ rendering_domain_(this),
+ runtime_domain_(this),
+ security_domain_(this),
+ service_worker_domain_(this),
+ tracing_domain_(this),
+ worker_domain_(this) {}
+
+HeadlessDevToolsClientImpl::~HeadlessDevToolsClientImpl() {}
+
+void HeadlessDevToolsClientImpl::AttachToHost(
+ content::DevToolsAgentHost* agent_host) {
+ DCHECK(!agent_host_);
+ agent_host_ = agent_host;
+ agent_host_->AttachClient(this);
+}
+
+void HeadlessDevToolsClientImpl::DetachFromHost(
+ content::DevToolsAgentHost* agent_host) {
+ DCHECK_EQ(agent_host_, agent_host);
+ agent_host_->DetachClient();
+ agent_host_ = nullptr;
+ pending_messages_.clear();
+}
+
+void HeadlessDevToolsClientImpl::DispatchProtocolMessage(
+ content::DevToolsAgentHost* agent_host,
+ const std::string& json_message) {
+ DCHECK_EQ(agent_host_, agent_host);
+ std::unique_ptr<base::Value> message =
+ base::JSONReader::Read(json_message, base::JSON_PARSE_RFC);
+ const base::DictionaryValue* message_dict;
+ if (!message || !message->GetAsDictionary(&message_dict)) {
+ NOTREACHED() << "Badly formed reply";
+ return;
+ }
+ if (!DispatchMessageReply(*message_dict) && !DispatchEvent(*message_dict))
+ DLOG(ERROR) << "Unhandled protocol message: " << json_message;
+}
+
+bool HeadlessDevToolsClientImpl::DispatchMessageReply(
+ const base::DictionaryValue& message_dict) {
+ int id = 0;
+ if (!message_dict.GetInteger("id", &id))
+ return false;
+ auto it = pending_messages_.find(id);
+ if (it == pending_messages_.end()) {
+ NOTREACHED() << "Unexpected reply";
+ return false;
+ }
+ Callback callback = std::move(it->second);
+ 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";
+ return false;
+ }
+ callback.callback_with_result.Run(*result_dict);
+ } else if (!callback.callback.is_null()) {
+ callback.callback.Run();
+ }
+ return true;
+}
+
+bool HeadlessDevToolsClientImpl::DispatchEvent(
+ const base::DictionaryValue& message_dict) {
+ std::string method;
+ if (!message_dict.GetString("method", &method))
+ return false;
+ auto it = event_handlers_.find(method);
+ if (it == event_handlers_.end()) {
+ NOTREACHED() << "Unknown event: " << method;
+ return false;
+ }
+ if (!it->second.is_null()) {
+ const base::DictionaryValue* result_dict;
+ if (!message_dict.GetDictionary("params", &result_dict)) {
+ NOTREACHED() << "Badly formed event parameters";
+ return false;
+ }
+ it->second.Run(*result_dict);
+ }
+ return true;
+}
+
+void HeadlessDevToolsClientImpl::AgentHostClosed(
+ content::DevToolsAgentHost* agent_host,
+ bool replaced_with_another_client) {
+ DCHECK_EQ(agent_host_, agent_host);
+ agent_host = nullptr;
+ pending_messages_.clear();
+}
+
+accessibility::Domain* HeadlessDevToolsClientImpl::GetAccessibility() {
+ return &accessibility_domain_;
+}
+
+animation::Domain* HeadlessDevToolsClientImpl::GetAnimation() {
+ return &animation_domain_;
+}
+
+application_cache::Domain* HeadlessDevToolsClientImpl::GetApplicationCache() {
+ return &application_cache_domain_;
+}
+
+cache_storage::Domain* HeadlessDevToolsClientImpl::GetCacheStorage() {
+ return &cache_storage_domain_;
+}
+
+console::Domain* HeadlessDevToolsClientImpl::GetConsole() {
+ return &console_domain_;
+}
+
+css::Domain* HeadlessDevToolsClientImpl::GetCSS() {
+ return &css_domain_;
+}
+
+database::Domain* HeadlessDevToolsClientImpl::GetDatabase() {
+ return &database_domain_;
+}
+
+debugger::Domain* HeadlessDevToolsClientImpl::GetDebugger() {
+ return &debugger_domain_;
+}
+
+device_orientation::Domain* HeadlessDevToolsClientImpl::GetDeviceOrientation() {
+ return &device_orientation_domain_;
+}
+
+dom_debugger::Domain* HeadlessDevToolsClientImpl::GetDOMDebugger() {
+ return &dom_debugger_domain_;
+}
+
+dom::Domain* HeadlessDevToolsClientImpl::GetDOM() {
+ return &dom_domain_;
+}
+
+dom_storage::Domain* HeadlessDevToolsClientImpl::GetDOMStorage() {
+ return &dom_storage_domain_;
+}
+
+emulation::Domain* HeadlessDevToolsClientImpl::GetEmulation() {
+ return &emulation_domain_;
+}
+
+heap_profiler::Domain* HeadlessDevToolsClientImpl::GetHeapProfiler() {
+ return &heap_profiler_domain_;
+}
+
+indexeddb::Domain* HeadlessDevToolsClientImpl::GetIndexedDB() {
+ return &indexeddb_domain_;
+}
+
+input::Domain* HeadlessDevToolsClientImpl::GetInput() {
+ return &input_domain_;
+}
+
+inspector::Domain* HeadlessDevToolsClientImpl::GetInspector() {
+ return &inspector_domain_;
+}
+
+io::Domain* HeadlessDevToolsClientImpl::GetIO() {
+ return &io_domain_;
+}
+
+layer_tree::Domain* HeadlessDevToolsClientImpl::GetLayerTree() {
+ return &layer_tree_domain_;
+}
+
+memory::Domain* HeadlessDevToolsClientImpl::GetMemory() {
+ return &memory_domain_;
+}
+
+network::Domain* HeadlessDevToolsClientImpl::GetNetwork() {
+ return &network_domain_;
+}
+
+page::Domain* HeadlessDevToolsClientImpl::GetPage() {
+ return &page_domain_;
+}
+
+profiler::Domain* HeadlessDevToolsClientImpl::GetProfiler() {
+ return &profiler_domain_;
+}
+
+rendering::Domain* HeadlessDevToolsClientImpl::GetRendering() {
+ return &rendering_domain_;
+}
+
+runtime::Domain* HeadlessDevToolsClientImpl::GetRuntime() {
+ return &runtime_domain_;
+}
+
+security::Domain* HeadlessDevToolsClientImpl::GetSecurity() {
+ return &security_domain_;
+}
+
+service_worker::Domain* HeadlessDevToolsClientImpl::GetServiceWorker() {
+ return &service_worker_domain_;
+}
+
+tracing::Domain* HeadlessDevToolsClientImpl::GetTracing() {
+ return &tracing_domain_;
+}
+
+worker::Domain* HeadlessDevToolsClientImpl::GetWorker() {
+ return &worker_domain_;
+}
+
+template <typename CallbackType>
+void HeadlessDevToolsClientImpl::FinalizeAndSendMessage(
+ base::DictionaryValue* message,
+ CallbackType callback) {
+ DCHECK(agent_host_);
+ int id = next_message_id_++;
+ message->SetInteger("id", id);
+ std::string json_message;
+ base::JSONWriter::Write(*message, &json_message);
+ pending_messages_[id] = Callback(callback);
+ agent_host_->DispatchProtocolMessage(json_message);
+}
+
+template <typename CallbackType>
+void HeadlessDevToolsClientImpl::SendMessageWithParams(
+ const char* method,
+ std::unique_ptr<base::Value> params,
+ CallbackType callback) {
+ base::DictionaryValue message;
+ message.SetString("method", method);
+ message.Set("params", std::move(params));
+ FinalizeAndSendMessage(&message, std::move(callback));
+}
+
+template <typename CallbackType>
+void HeadlessDevToolsClientImpl::SendMessageWithoutParams(
+ const char* method,
+ CallbackType callback) {
+ base::DictionaryValue message;
+ message.SetString("method", method);
+ FinalizeAndSendMessage(&message, std::move(callback));
+}
+
+void HeadlessDevToolsClientImpl::SendMessage(
+ const char* method,
+ std::unique_ptr<base::Value> params,
+ base::Callback<void(const base::Value&)> callback) {
+ SendMessageWithParams(method, std::move(params), std::move(callback));
+}
+
+void HeadlessDevToolsClientImpl::SendMessage(
+ const char* method,
+ std::unique_ptr<base::Value> params,
+ base::Callback<void()> callback) {
+ SendMessageWithParams(method, std::move(params), std::move(callback));
+}
+
+void HeadlessDevToolsClientImpl::SendMessage(
+ const char* method,
+ base::Callback<void(const base::Value&)> callback) {
+ SendMessageWithoutParams(method, std::move(callback));
+}
+
+void HeadlessDevToolsClientImpl::SendMessage(const char* method,
+ base::Callback<void()> callback) {
+ SendMessageWithoutParams(method, std::move(callback));
+}
+
+void HeadlessDevToolsClientImpl::RegisterEventHandler(
+ const char* method,
+ base::Callback<void(const base::Value&)> callback) {
+ DCHECK(event_handlers_.find(method) == event_handlers_.end());
+ event_handlers_[method] = callback;
+}
+
+HeadlessDevToolsClientImpl::Callback::Callback() {}
+
+HeadlessDevToolsClientImpl::Callback::Callback(Callback&& other) = default;
+
+HeadlessDevToolsClientImpl::Callback::Callback(base::Callback<void()> callback)
+ : callback(callback) {}
+
+HeadlessDevToolsClientImpl::Callback::Callback(
+ base::Callback<void(const base::Value&)> callback)
+ : callback_with_result(callback) {}
+
+HeadlessDevToolsClientImpl::Callback::~Callback() {}
+
+HeadlessDevToolsClientImpl::Callback& HeadlessDevToolsClientImpl::Callback::
+operator=(Callback&& other) = default;
+
+} // namespace headless
diff --git a/chromium/headless/lib/browser/headless_devtools_client_impl.h b/chromium/headless/lib/browser/headless_devtools_client_impl.h
new file mode 100644
index 00000000000..c7dbb52db45
--- /dev/null
+++ b/chromium/headless/lib/browser/headless_devtools_client_impl.h
@@ -0,0 +1,189 @@
+// 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_DEVTOOLS_CLIENT_IMPL_H_
+#define HEADLESS_LIB_BROWSER_HEADLESS_DEVTOOLS_CLIENT_IMPL_H_
+
+#include <unordered_map>
+
+#include "content/public/browser/devtools_agent_host_client.h"
+#include "headless/public/domains/accessibility.h"
+#include "headless/public/domains/animation.h"
+#include "headless/public/domains/application_cache.h"
+#include "headless/public/domains/cache_storage.h"
+#include "headless/public/domains/console.h"
+#include "headless/public/domains/css.h"
+#include "headless/public/domains/database.h"
+#include "headless/public/domains/debugger.h"
+#include "headless/public/domains/device_orientation.h"
+#include "headless/public/domains/dom.h"
+#include "headless/public/domains/dom_debugger.h"
+#include "headless/public/domains/dom_storage.h"
+#include "headless/public/domains/emulation.h"
+#include "headless/public/domains/heap_profiler.h"
+#include "headless/public/domains/indexeddb.h"
+#include "headless/public/domains/input.h"
+#include "headless/public/domains/inspector.h"
+#include "headless/public/domains/io.h"
+#include "headless/public/domains/layer_tree.h"
+#include "headless/public/domains/memory.h"
+#include "headless/public/domains/network.h"
+#include "headless/public/domains/page.h"
+#include "headless/public/domains/profiler.h"
+#include "headless/public/domains/rendering.h"
+#include "headless/public/domains/runtime.h"
+#include "headless/public/domains/security.h"
+#include "headless/public/domains/service_worker.h"
+#include "headless/public/domains/tracing.h"
+#include "headless/public/domains/worker.h"
+#include "headless/public/headless_devtools_client.h"
+#include "headless/public/internal/message_dispatcher.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace content {
+class DevToolsAgentHost;
+}
+
+namespace headless {
+
+class HeadlessDevToolsClientImpl : public HeadlessDevToolsClient,
+ public content::DevToolsAgentHostClient,
+ public internal::MessageDispatcher {
+ public:
+ HeadlessDevToolsClientImpl();
+ ~HeadlessDevToolsClientImpl() override;
+
+ static HeadlessDevToolsClientImpl* From(HeadlessDevToolsClient* client);
+
+ // HeadlessDevToolsClient implementation:
+ accessibility::Domain* GetAccessibility() override;
+ animation::Domain* GetAnimation() override;
+ application_cache::Domain* GetApplicationCache() override;
+ cache_storage::Domain* GetCacheStorage() override;
+ console::Domain* GetConsole() override;
+ css::Domain* GetCSS() override;
+ database::Domain* GetDatabase() override;
+ debugger::Domain* GetDebugger() override;
+ device_orientation::Domain* GetDeviceOrientation() override;
+ dom::Domain* GetDOM() override;
+ dom_debugger::Domain* GetDOMDebugger() override;
+ dom_storage::Domain* GetDOMStorage() override;
+ emulation::Domain* GetEmulation() override;
+ heap_profiler::Domain* GetHeapProfiler() override;
+ indexeddb::Domain* GetIndexedDB() override;
+ input::Domain* GetInput() override;
+ inspector::Domain* GetInspector() override;
+ io::Domain* GetIO() override;
+ layer_tree::Domain* GetLayerTree() override;
+ memory::Domain* GetMemory() override;
+ network::Domain* GetNetwork() override;
+ page::Domain* GetPage() override;
+ profiler::Domain* GetProfiler() override;
+ rendering::Domain* GetRendering() override;
+ runtime::Domain* GetRuntime() override;
+ security::Domain* GetSecurity() override;
+ service_worker::Domain* GetServiceWorker() override;
+ tracing::Domain* GetTracing() override;
+ worker::Domain* GetWorker() override;
+
+ // content::DevToolstAgentHostClient implementation:
+ void DispatchProtocolMessage(content::DevToolsAgentHost* agent_host,
+ const std::string& json_message) override;
+ void AgentHostClosed(content::DevToolsAgentHost* agent_host,
+ bool replaced_with_another_client) override;
+
+ // internal::MessageDispatcher implementation:
+ void SendMessage(const char* method,
+ std::unique_ptr<base::Value> params,
+ base::Callback<void(const base::Value&)> callback) override;
+ void SendMessage(const char* method,
+ std::unique_ptr<base::Value> params,
+ base::Callback<void()> callback) override;
+ void SendMessage(const char* method,
+ base::Callback<void(const base::Value&)> callback) override;
+ void SendMessage(const char* method,
+ base::Callback<void()> callback) override;
+ void RegisterEventHandler(
+ const char* method,
+ base::Callback<void(const base::Value&)> callback) override;
+
+ void AttachToHost(content::DevToolsAgentHost* agent_host);
+ void DetachFromHost(content::DevToolsAgentHost* agent_host);
+
+ private:
+ // Contains a callback with or without a result parameter depending on the
+ // message type.
+ struct Callback {
+ Callback();
+ Callback(Callback&& other);
+ explicit Callback(base::Callback<void()> callback);
+ explicit Callback(base::Callback<void(const base::Value&)> callback);
+ ~Callback();
+
+ Callback& operator=(Callback&& other);
+
+ base::Callback<void()> callback;
+ base::Callback<void(const base::Value&)> callback_with_result;
+ };
+
+ template <typename CallbackType>
+ void FinalizeAndSendMessage(base::DictionaryValue* message,
+ CallbackType callback);
+
+ template <typename CallbackType>
+ void SendMessageWithParams(const char* method,
+ std::unique_ptr<base::Value> params,
+ CallbackType callback);
+
+ template <typename CallbackType>
+ void SendMessageWithoutParams(const char* method, CallbackType callback);
+
+ bool DispatchMessageReply(const base::DictionaryValue& message_dict);
+ bool DispatchEvent(const base::DictionaryValue& message_dict);
+
+ content::DevToolsAgentHost* agent_host_; // Not owned.
+ int next_message_id_;
+ std::unordered_map<int, Callback> pending_messages_;
+ std::unordered_map<std::string, base::Callback<void(const base::Value&)>>
+ event_handlers_;
+
+ accessibility::ExperimentalDomain accessibility_domain_;
+ animation::ExperimentalDomain animation_domain_;
+ application_cache::ExperimentalDomain application_cache_domain_;
+ cache_storage::ExperimentalDomain cache_storage_domain_;
+ console::ExperimentalDomain console_domain_;
+ css::ExperimentalDomain css_domain_;
+ database::ExperimentalDomain database_domain_;
+ debugger::ExperimentalDomain debugger_domain_;
+ device_orientation::ExperimentalDomain device_orientation_domain_;
+ dom_debugger::ExperimentalDomain dom_debugger_domain_;
+ dom::ExperimentalDomain dom_domain_;
+ dom_storage::ExperimentalDomain dom_storage_domain_;
+ emulation::ExperimentalDomain emulation_domain_;
+ heap_profiler::ExperimentalDomain heap_profiler_domain_;
+ indexeddb::ExperimentalDomain indexeddb_domain_;
+ input::ExperimentalDomain input_domain_;
+ inspector::ExperimentalDomain inspector_domain_;
+ io::ExperimentalDomain io_domain_;
+ layer_tree::ExperimentalDomain layer_tree_domain_;
+ memory::ExperimentalDomain memory_domain_;
+ network::ExperimentalDomain network_domain_;
+ page::ExperimentalDomain page_domain_;
+ profiler::ExperimentalDomain profiler_domain_;
+ rendering::ExperimentalDomain rendering_domain_;
+ runtime::ExperimentalDomain runtime_domain_;
+ security::ExperimentalDomain security_domain_;
+ service_worker::ExperimentalDomain service_worker_domain_;
+ tracing::ExperimentalDomain tracing_domain_;
+ worker::ExperimentalDomain worker_domain_;
+
+ DISALLOW_COPY_AND_ASSIGN(HeadlessDevToolsClientImpl);
+};
+
+} // namespace headless
+
+#endif // HEADLESS_LIB_BROWSER_HEADLESS_DEVTOOLS_CLIENT_IMPL_H_
diff --git a/chromium/headless/lib/browser/headless_screen.cc b/chromium/headless/lib/browser/headless_screen.cc
index 6d6f6074c73..e7be71a515b 100644
--- a/chromium/headless/lib/browser/headless_screen.cc
+++ b/chromium/headless/lib/browser/headless_screen.cc
@@ -12,18 +12,18 @@
#include "ui/aura/window_event_dispatcher.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/ime/input_method.h"
+#include "ui/display/screen.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/geometry/size_conversions.h"
#include "ui/gfx/native_widget_types.h"
-#include "ui/gfx/screen.h"
namespace headless {
namespace {
-bool IsRotationPortrait(gfx::Display::Rotation rotation) {
- return rotation == gfx::Display::ROTATE_90 ||
- rotation == gfx::Display::ROTATE_270;
+bool IsRotationPortrait(display::Display::Rotation rotation) {
+ return rotation == display::Display::ROTATE_90 ||
+ rotation == display::Display::ROTATE_270;
}
} // namespace
@@ -52,7 +52,7 @@ void HeadlessScreen::SetDeviceScaleFactor(float device_scale_factor) {
display_.SetScaleAndBounds(device_scale_factor, bounds_in_pixel);
}
-void HeadlessScreen::SetDisplayRotation(gfx::Display::Rotation rotation) {
+void HeadlessScreen::SetDisplayRotation(display::Display::Rotation rotation) {
gfx::Rect bounds_in_pixel(display_.GetSizeInPixel());
gfx::Rect new_bounds(bounds_in_pixel);
if (IsRotationPortrait(rotation) != IsRotationPortrait(display_.rotation())) {
@@ -80,17 +80,17 @@ void HeadlessScreen::SetWorkAreaInsets(const gfx::Insets& insets) {
gfx::Transform HeadlessScreen::GetRotationTransform() const {
gfx::Transform rotate;
switch (display_.rotation()) {
- case gfx::Display::ROTATE_0:
+ case display::Display::ROTATE_0:
break;
- case gfx::Display::ROTATE_90:
+ case display::Display::ROTATE_90:
rotate.Translate(display_.bounds().height(), 0);
rotate.Rotate(90);
break;
- case gfx::Display::ROTATE_270:
+ case display::Display::ROTATE_270:
rotate.Translate(0, display_.bounds().width());
rotate.Rotate(270);
break;
- case gfx::Display::ROTATE_180:
+ case display::Display::ROTATE_180:
rotate.Translate(display_.bounds().width(), display_.bounds().height());
rotate.Rotate(180);
break;
@@ -122,8 +122,8 @@ gfx::Point HeadlessScreen::GetCursorScreenPoint() {
return aura::Env::GetInstance()->last_mouse_location();
}
-gfx::NativeWindow HeadlessScreen::GetWindowUnderCursor() {
- return GetWindowAtScreenPoint(GetCursorScreenPoint());
+bool HeadlessScreen::IsWindowUnderCursor(gfx::NativeWindow window) {
+ return GetWindowAtScreenPoint(GetCursorScreenPoint()) == window;
}
gfx::NativeWindow HeadlessScreen::GetWindowAtScreenPoint(
@@ -137,32 +137,32 @@ int HeadlessScreen::GetNumDisplays() const {
return 1;
}
-std::vector<gfx::Display> HeadlessScreen::GetAllDisplays() const {
- return std::vector<gfx::Display>(1, display_);
+std::vector<display::Display> HeadlessScreen::GetAllDisplays() const {
+ return std::vector<display::Display>(1, display_);
}
-gfx::Display HeadlessScreen::GetDisplayNearestWindow(
+display::Display HeadlessScreen::GetDisplayNearestWindow(
gfx::NativeWindow window) const {
return display_;
}
-gfx::Display HeadlessScreen::GetDisplayNearestPoint(
+display::Display HeadlessScreen::GetDisplayNearestPoint(
const gfx::Point& point) const {
return display_;
}
-gfx::Display HeadlessScreen::GetDisplayMatching(
+display::Display HeadlessScreen::GetDisplayMatching(
const gfx::Rect& match_rect) const {
return display_;
}
-gfx::Display HeadlessScreen::GetPrimaryDisplay() const {
+display::Display HeadlessScreen::GetPrimaryDisplay() const {
return display_;
}
-void HeadlessScreen::AddObserver(gfx::DisplayObserver* observer) {}
+void HeadlessScreen::AddObserver(display::DisplayObserver* observer) {}
-void HeadlessScreen::RemoveObserver(gfx::DisplayObserver* observer) {}
+void HeadlessScreen::RemoveObserver(display::DisplayObserver* observer) {}
HeadlessScreen::HeadlessScreen(const gfx::Rect& screen_bounds)
: host_(NULL), ui_scale_(1.0f) {
diff --git a/chromium/headless/lib/browser/headless_screen.h b/chromium/headless/lib/browser/headless_screen.h
index c6b6b3be01b..f0c363c54f6 100644
--- a/chromium/headless/lib/browser/headless_screen.h
+++ b/chromium/headless/lib/browser/headless_screen.h
@@ -8,8 +8,8 @@
#include "base/compiler_specific.h"
#include "base/macros.h"
#include "ui/aura/window_observer.h"
-#include "ui/gfx/display.h"
-#include "ui/gfx/screen.h"
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
namespace gfx {
class Insets;
@@ -19,22 +19,23 @@ class Transform;
namespace aura {
class Window;
+class WindowTreeClient;
class WindowTreeHost;
}
namespace headless {
-class HeadlessScreen : public gfx::Screen, public aura::WindowObserver {
+class HeadlessScreen : public display::Screen, public aura::WindowObserver {
public:
- // Creates a gfx::Screen of the specified size. If no size is specified, then
- // creates a 800x600 screen. |size| is in physical pixels.
+ // Creates a display::Screen of the specified size. If no size is specified,
+ // then creates a 800x600 screen. |size| is in physical pixels.
static HeadlessScreen* Create(const gfx::Size& size);
~HeadlessScreen() override;
aura::WindowTreeHost* CreateHostForPrimaryDisplay();
void SetDeviceScaleFactor(float device_scale_fator);
- void SetDisplayRotation(gfx::Display::Rotation rotation);
+ void SetDisplayRotation(display::Display::Rotation rotation);
void SetUIScale(float ui_scale);
void SetWorkAreaInsets(const gfx::Insets& insets);
@@ -48,24 +49,26 @@ class HeadlessScreen : public gfx::Screen, public aura::WindowObserver {
const gfx::Rect& new_bounds) override;
void OnWindowDestroying(aura::Window* window) override;
- // gfx::Screen overrides:
+ // display::Screen overrides:
gfx::Point GetCursorScreenPoint() override;
- gfx::NativeWindow GetWindowUnderCursor() override;
+ bool IsWindowUnderCursor(gfx::NativeWindow window) override;
gfx::NativeWindow GetWindowAtScreenPoint(const gfx::Point& point) override;
int GetNumDisplays() const override;
- std::vector<gfx::Display> GetAllDisplays() const override;
- gfx::Display GetDisplayNearestWindow(gfx::NativeView view) const override;
- gfx::Display GetDisplayNearestPoint(const gfx::Point& point) const override;
- gfx::Display GetDisplayMatching(const gfx::Rect& match_rect) const override;
- gfx::Display GetPrimaryDisplay() const override;
- void AddObserver(gfx::DisplayObserver* observer) override;
- void RemoveObserver(gfx::DisplayObserver* observer) override;
+ std::vector<display::Display> GetAllDisplays() const override;
+ display::Display GetDisplayNearestWindow(gfx::NativeView view) const override;
+ display::Display GetDisplayNearestPoint(
+ const gfx::Point& point) const override;
+ display::Display GetDisplayMatching(
+ const gfx::Rect& match_rect) const override;
+ display::Display GetPrimaryDisplay() const override;
+ void AddObserver(display::DisplayObserver* observer) override;
+ void RemoveObserver(display::DisplayObserver* observer) override;
private:
explicit HeadlessScreen(const gfx::Rect& screen_bounds);
aura::WindowTreeHost* host_;
- gfx::Display display_;
+ display::Display display_;
float ui_scale_;
DISALLOW_COPY_AND_ASSIGN(HeadlessScreen);
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 1a46e1064a1..418b63f3416 100644
--- a/chromium/headless/lib/browser/headless_url_request_context_getter.cc
+++ b/chromium/headless/lib/browser/headless_url_request_context_getter.cc
@@ -6,7 +6,6 @@
#include <memory>
-#include "base/command_line.h"
#include "base/memory/ptr_util.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/worker_pool.h"
@@ -105,9 +104,6 @@ HeadlessURLRequestContextGetter::GetURLRequestContext() {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
if (!url_request_context_) {
- const base::CommandLine& command_line =
- *base::CommandLine::ForCurrentProcess();
-
url_request_context_.reset(new net::URLRequestContext());
url_request_context_->set_net_log(net_log_);
network_delegate_ = CreateNetworkDelegate();
@@ -162,11 +158,10 @@ HeadlessURLRequestContextGetter::GetURLRequestContext() {
network_session_params.net_log = url_request_context_->net_log();
network_session_params.ignore_certificate_errors =
ignore_certificate_errors_;
- if (command_line.HasSwitch(switches::kHostResolverRules)) {
+ if (!options_.host_resolver_rules.empty()) {
std::unique_ptr<net::MappedHostResolver> mapped_host_resolver(
new net::MappedHostResolver(std::move(host_resolver)));
- mapped_host_resolver->SetRulesFromString(
- command_line.GetSwitchValueASCII(switches::kHostResolverRules));
+ mapped_host_resolver->SetRulesFromString(options_.host_resolver_rules);
host_resolver = std::move(mapped_host_resolver);
}
diff --git a/chromium/headless/lib/browser/headless_web_contents_impl.cc b/chromium/headless/lib/browser/headless_web_contents_impl.cc
index f58dfe0671c..13ed5e1076c 100644
--- a/chromium/headless/lib/browser/headless_web_contents_impl.cc
+++ b/chromium/headless/lib/browser/headless_web_contents_impl.cc
@@ -8,6 +8,7 @@
#include "base/memory/ptr_util.h"
#include "base/memory/weak_ptr.h"
#include "base/trace_event/trace_event.h"
+#include "content/public/browser/devtools_agent_host.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
@@ -19,6 +20,8 @@
#include "content/public/common/bindings_policy.h"
#include "content/public/common/service_registry.h"
#include "content/public/renderer/render_frame.h"
+#include "headless/lib/browser/headless_browser_impl.h"
+#include "headless/lib/browser/headless_devtools_client_impl.h"
#include "ui/aura/window.h"
namespace headless {
@@ -31,16 +34,9 @@ class WebContentsObserverAdapter : public content::WebContentsObserver {
~WebContentsObserverAdapter() override {}
- void RenderViewReady() override { observer_->WebContentsReady(); }
-
- void DocumentOnLoadCompletedInMainFrame() override {
- observer_->DocumentOnLoadCompletedInMainFrame();
- }
-
- void DidFinishNavigation(
- content::NavigationHandle* navigation_handle) override {
- observer_->DidFinishNavigation(navigation_handle->HasCommitted() &&
- !navigation_handle->IsErrorPage());
+ void RenderViewReady() override {
+ DCHECK(web_contents()->GetMainFrame()->IsRenderFrameLive());
+ observer_->DevToolsTargetReady();
}
private:
@@ -51,23 +47,53 @@ class WebContentsObserverAdapter : public content::WebContentsObserver {
class HeadlessWebContentsImpl::Delegate : public content::WebContentsDelegate {
public:
- Delegate() {}
+ explicit Delegate(HeadlessBrowserImpl* browser) : browser_(browser) {}
+
+ void WebContentsCreated(content::WebContents* source_contents,
+ int opener_render_frame_id,
+ const std::string& frame_name,
+ const GURL& target_url,
+ content::WebContents* new_contents) override {
+ browser_->RegisterWebContents(
+ HeadlessWebContentsImpl::CreateFromWebContents(new_contents, browser_));
+ }
private:
+ HeadlessBrowserImpl* browser_; // Not owned.
DISALLOW_COPY_AND_ASSIGN(Delegate);
};
-HeadlessWebContentsImpl::HeadlessWebContentsImpl(
- content::BrowserContext* browser_context,
+// static
+std::unique_ptr<HeadlessWebContentsImpl> HeadlessWebContentsImpl::Create(
+ content::BrowserContext* context,
aura::Window* parent_window,
- const gfx::Size& initial_size)
- : web_contents_delegate_(new HeadlessWebContentsImpl::Delegate()) {
- content::WebContents::CreateParams create_params(browser_context, nullptr);
+ const gfx::Size& initial_size,
+ HeadlessBrowserImpl* browser) {
+ content::WebContents::CreateParams create_params(context, nullptr);
create_params.initial_size = initial_size;
- web_contents_.reset(content::WebContents::Create(create_params));
- web_contents_->SetDelegate(web_contents_delegate_.get());
+ std::unique_ptr<HeadlessWebContentsImpl> headless_web_contents =
+ base::WrapUnique(new HeadlessWebContentsImpl(
+ content::WebContents::Create(create_params), browser));
+
+ headless_web_contents->InitializeScreen(parent_window, initial_size);
+
+ return headless_web_contents;
+}
+
+// static
+std::unique_ptr<HeadlessWebContentsImpl>
+HeadlessWebContentsImpl::CreateFromWebContents(
+ content::WebContents* web_contents,
+ HeadlessBrowserImpl* browser) {
+ std::unique_ptr<HeadlessWebContentsImpl> headless_web_contents =
+ base::WrapUnique(new HeadlessWebContentsImpl(web_contents, browser));
+
+ return headless_web_contents;
+}
+void HeadlessWebContentsImpl::InitializeScreen(aura::Window* parent_window,
+ const gfx::Size& initial_size) {
aura::Window* contents = web_contents_->GetNativeView();
DCHECK(!parent_window->Contains(contents));
parent_window->AddChild(contents);
@@ -80,6 +106,15 @@ HeadlessWebContentsImpl::HeadlessWebContentsImpl(
host_view->SetSize(initial_size);
}
+HeadlessWebContentsImpl::HeadlessWebContentsImpl(
+ content::WebContents* web_contents,
+ HeadlessBrowserImpl* browser)
+ : web_contents_delegate_(new HeadlessWebContentsImpl::Delegate(browser)),
+ web_contents_(web_contents),
+ browser_(browser) {
+ web_contents_->SetDelegate(web_contents_delegate_.get());
+}
+
HeadlessWebContentsImpl::~HeadlessWebContentsImpl() {
web_contents_->Close();
}
@@ -95,6 +130,10 @@ bool HeadlessWebContentsImpl::OpenURL(const GURL& url) {
return true;
}
+void HeadlessWebContentsImpl::Close() {
+ browser_->DestroyWebContents(this);
+}
+
void HeadlessWebContentsImpl::AddObserver(Observer* observer) {
DCHECK(observer_map_.find(observer) == observer_map_.end());
observer_map_[observer] = base::WrapUnique(
@@ -107,6 +146,21 @@ void HeadlessWebContentsImpl::RemoveObserver(Observer* observer) {
observer_map_.erase(it);
}
+HeadlessDevToolsTarget* HeadlessWebContentsImpl::GetDevToolsTarget() {
+ return web_contents()->GetMainFrame()->IsRenderFrameLive() ? this : nullptr;
+}
+
+void HeadlessWebContentsImpl::AttachClient(HeadlessDevToolsClient* client) {
+ if (!agent_host_)
+ agent_host_ = content::DevToolsAgentHost::GetOrCreateFor(web_contents());
+ HeadlessDevToolsClientImpl::From(client)->AttachToHost(agent_host_.get());
+}
+
+void HeadlessWebContentsImpl::DetachClient(HeadlessDevToolsClient* client) {
+ DCHECK(agent_host_);
+ HeadlessDevToolsClientImpl::From(client)->DetachFromHost(agent_host_.get());
+}
+
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 e1cdc2d1615..3d2b7654361 100644
--- a/chromium/headless/lib/browser/headless_web_contents_impl.h
+++ b/chromium/headless/lib/browser/headless_web_contents_impl.h
@@ -5,6 +5,7 @@
#ifndef HEADLESS_LIB_BROWSER_HEADLESS_WEB_CONTENTS_IMPL_H_
#define HEADLESS_LIB_BROWSER_HEADLESS_WEB_CONTENTS_IMPL_H_
+#include "headless/public/headless_devtools_target.h"
#include "headless/public/headless_web_contents.h"
#include <memory>
@@ -15,8 +16,9 @@ class Window;
}
namespace content {
-class WebContents;
class BrowserContext;
+class DevToolsAgentHost;
+class WebContents;
}
namespace gfx {
@@ -24,26 +26,54 @@ class Size;
}
namespace headless {
+class HeadlessDevToolsHostImpl;
+class HeadlessBrowserImpl;
class WebContentsObserverAdapter;
-class HeadlessWebContentsImpl : public HeadlessWebContents {
+class HeadlessWebContentsImpl : public HeadlessWebContents,
+ public HeadlessDevToolsTarget {
public:
- HeadlessWebContentsImpl(content::BrowserContext* context,
- aura::Window* parent_window,
- const gfx::Size& initial_size);
~HeadlessWebContentsImpl() override;
+ static std::unique_ptr<HeadlessWebContentsImpl> Create(
+ content::BrowserContext* context,
+ aura::Window* parent_window,
+ const gfx::Size& initial_size,
+ HeadlessBrowserImpl* browser);
+
+ // Takes ownership of |web_contents|.
+ static std::unique_ptr<HeadlessWebContentsImpl> CreateFromWebContents(
+ content::WebContents* web_contents,
+ HeadlessBrowserImpl* browser);
+
// HeadlessWebContents implementation:
void AddObserver(Observer* observer) override;
void RemoveObserver(Observer* observer) override;
+ HeadlessDevToolsTarget* GetDevToolsTarget() override;
+
+ // HeadlessDevToolsTarget implementation:
+ void AttachClient(HeadlessDevToolsClient* client) override;
+ void DetachClient(HeadlessDevToolsClient* client) override;
content::WebContents* web_contents() const;
bool OpenURL(const GURL& url);
+ void Close() override;
+
private:
+ // Takes ownership of |web_contents|.
+ HeadlessWebContentsImpl(content::WebContents* web_contents,
+ HeadlessBrowserImpl* browser);
+
+ void InitializeScreen(aura::Window* parent_window,
+ const gfx::Size& initial_size);
+
class Delegate;
std::unique_ptr<Delegate> web_contents_delegate_;
std::unique_ptr<content::WebContents> web_contents_;
+ scoped_refptr<content::DevToolsAgentHost> agent_host_;
+
+ HeadlessBrowserImpl* browser_; // Not owned.
using ObserverMap =
std::unordered_map<HeadlessWebContents::Observer*,
diff --git a/chromium/headless/lib/browser/headless_window_tree_client.cc b/chromium/headless/lib/browser/headless_window_tree_client.cc
new file mode 100644
index 00000000000..be797ce3032
--- /dev/null
+++ b/chromium/headless/lib/browser/headless_window_tree_client.cc
@@ -0,0 +1,27 @@
+// Copyright (c) 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_client.h"
+
+#include "ui/aura/window.h"
+
+namespace headless {
+
+HeadlessWindowTreeClient::HeadlessWindowTreeClient(aura::Window* root_window)
+ : root_window_(root_window) {
+ aura::client::SetWindowTreeClient(root_window_, this);
+}
+
+HeadlessWindowTreeClient::~HeadlessWindowTreeClient() {
+ aura::client::SetWindowTreeClient(root_window_, nullptr);
+}
+
+aura::Window* HeadlessWindowTreeClient::GetDefaultParent(
+ aura::Window* context,
+ aura::Window* window,
+ const gfx::Rect& bounds) {
+ return root_window_;
+}
+
+} // namespace headless
diff --git a/chromium/headless/lib/browser/headless_window_tree_client.h b/chromium/headless/lib/browser/headless_window_tree_client.h
new file mode 100644
index 00000000000..c64e3daf92b
--- /dev/null
+++ b/chromium/headless/lib/browser/headless_window_tree_client.h
@@ -0,0 +1,30 @@
+// Copyright (c) 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_CLIENT_H_
+#define HEADLESS_LIB_BROWSER_HEADLESS_WINDOW_TREE_CLIENT_H_
+
+#include "base/macros.h"
+#include "ui/aura/client/window_tree_client.h"
+
+namespace headless {
+
+class HeadlessWindowTreeClient : public aura::client::WindowTreeClient {
+ public:
+ explicit HeadlessWindowTreeClient(aura::Window* root_window);
+ ~HeadlessWindowTreeClient() override;
+
+ aura::Window* GetDefaultParent(aura::Window* context,
+ aura::Window* window,
+ const gfx::Rect& bounds) override;
+
+ private:
+ aura::Window* root_window_; // Not owned.
+
+ DISALLOW_COPY_AND_ASSIGN(HeadlessWindowTreeClient);
+};
+
+} // namespace headless
+
+#endif // HEADLESS_LIB_BROWSER_HEADLESS_WINDOW_TREE_CLIENT_H_
diff --git a/chromium/headless/lib/browser/type_conversions_h.template b/chromium/headless/lib/browser/type_conversions_h.template
new file mode 100644
index 00000000000..286b0e5ea65
--- /dev/null
+++ b/chromium/headless/lib/browser/type_conversions_h.template
@@ -0,0 +1,77 @@
+// This file is generated
+
+// 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_PUBLIC_DOMAINS_TYPE_CONVERSIONS_H_
+#define HEADLESS_PUBLIC_DOMAINS_TYPE_CONVERSIONS_H_
+
+#include "headless/public/domains/types.h"
+#include "headless/public/internal/value_conversions.h"
+
+namespace headless {
+namespace internal {
+
+{% for domain in api.domains %}
+ {% for type in domain.types %}
+ {% set namespace = domain.domain | camelcase_to_hacker_style %}
+ {% if "enum" in type %}
+template <>
+struct FromValue<{{namespace}}::{{type.id}}> {
+ static {{namespace}}::{{type.id}} Parse(const base::Value& value, ErrorReporter* errors) {
+ {% set default = namespace + '::' + type.id + '::' + type.enum[0] | dash_to_camelcase | camelcase_to_hacker_style | upper | mangle_enum %}
+ std::string string_value;
+ if (!value.GetAsString(&string_value)) {
+ errors->AddError("string enum value expected");
+ {# Return an arbitrary enum member -- the caller will just ignore it. #}
+ return {{default}};
+ }
+ {% for literal in type.enum %}
+ if (string_value == "{{literal}}")
+ return {{namespace}}::{{type.id}}::{{literal | dash_to_camelcase | camelcase_to_hacker_style | upper | mangle_enum}};
+ {% endfor %}
+ errors->AddError("invalid enum value");
+ return {{default}};
+ }
+};
+
+template <typename T>
+std::unique_ptr<base::Value> ToValueImpl(const {{namespace}}::{{type.id}}& value, T*) {
+ switch (value) {
+ {% for literal in type.enum %}
+ case {{namespace}}::{{type.id}}::{{literal | dash_to_camelcase | camelcase_to_hacker_style | upper | mangle_enum}}:
+ return base::WrapUnique(new base::StringValue("{{literal}}"));
+ {% endfor %}
+ };
+ NOTREACHED();
+ return nullptr;
+}
+ {% continue %}
+ {% endif %}
+
+ {% if not (type.type == "object") or not ("properties" in type) %}{% continue %}{% endif %}
+template <>
+struct FromValue<{{namespace}}::{{type.id}}> {
+ static std::unique_ptr<{{namespace}}::{{type.id}}> Parse(const base::Value& value, ErrorReporter* errors) {
+ return {{namespace}}::{{type.id}}::Parse(value, errors);
+ }
+};
+
+template <typename T>
+std::unique_ptr<base::Value> ToValueImpl(const {{namespace}}::{{type.id}}& value, T*) {
+ return value.Serialize();
+}
+
+ {% endfor %}
+{% endfor %}
+
+template <typename T>
+std::unique_ptr<base::Value> ToValue(const T& value) {
+ return ToValueImpl(value, static_cast<T*>(nullptr));
+}
+
+} // namespace internal
+} // namespace headless
+
+#endif // HEADLESS_PUBLIC_DOMAINS_TYPE_CONVERSIONS_H_
diff --git a/chromium/headless/lib/browser/types_cc.template b/chromium/headless/lib/browser/types_cc.template
new file mode 100644
index 00000000000..5be72cf7924
--- /dev/null
+++ b/chromium/headless/lib/browser/types_cc.template
@@ -0,0 +1,130 @@
+// This file is generated
+
+// 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/public/domains/types.h"
+
+#include "base/memory/ptr_util.h"
+#include "headless/public/domains/type_conversions.h"
+
+namespace headless {
+
+// ------------- Enum values from types.
+{% for domain in api.domains %}
+ {% continue %}
+
+namespace internal {
+
+ {% for type in domain.types %}
+// {{type}}
+ {% if type.type == "array" %}
+template <>
+struct FromValue<{{resolve_type(type).raw_type}}> {
+ static {{resolve_type(type).raw_type}} Parse(const base::Value& value, ErrorReporter* errors) {
+ {{resolve_type(type).raw_type}} result;
+ const base::ListValue* list;
+ if (!value.GetAsList(&list)) {
+ errors->AddError("list value expected");
+ return result;
+ }
+ errors->Push();
+ for (const auto& item : *list)
+ result.push_back(FromValue<{{resolve_type(type).raw_type}}::value_type>::Parse(*item, errors));
+ errors->Pop();
+ return result;
+ }
+};
+
+ {% continue %}
+ {% endif %}
+#}
+ {% if not (type.type == "object") or not ("properties" in type) %}{% continue %}{% endif %}
+ {% set namespace = domain.domain | camelcase_to_hacker_style %}
+template <>
+struct FromValue<{{namespace}}::{{type.id}}> {
+ static std::unique_ptr<{{namespace}}::{{type.id}}> Parse(const base::Value& value, ErrorReporter* errors) {
+ return {{namespace}}::{{type.id}}::Parse(value, errors);
+ }
+};
+
+template <typename T>
+std::unique_ptr<base::Value> ToValueImpl(const {{namespace}}::{{type.id}}& value, T*) {
+ return value.Serialize();
+}
+
+ {% endfor %}
+} // namespace internal
+{% endfor %}
+
+{% for domain in api.domains %}
+
+namespace {{domain.domain | camelcase_to_hacker_style}} {
+ {% for type in domain.types %}
+ {% if not (type.type == "object") or not ("properties" in type) %}{% continue %}{% endif %}
+
+std::unique_ptr<{{type.id}}> {{type.id}}::Parse(const base::Value& value, ErrorReporter* errors) {
+ errors->Push();
+ errors->SetName("{{type.id}}");
+ const base::DictionaryValue* object;
+ if (!value.GetAsDictionary(&object)) {
+ errors->AddError("object expected");
+ errors->Pop();
+ return nullptr;
+ }
+
+ std::unique_ptr<{{type.id}}> result(new {{type.id}}());
+ errors->Push();
+ errors->SetName("{{type.id}}");
+ {% for property in type.properties %}
+ {% set value_name = property.name | camelcase_to_hacker_style + "_value" %}
+ const base::Value* {{value_name}};
+ if (object->Get("{{property.name}}", &{{value_name}})) {
+ errors->SetName("{{property.name}}");
+ {% if property.optional %}
+ result->{{property.name | camelcase_to_hacker_style}}_ = internal::FromValue<{{resolve_type(property).raw_type}}>::Parse(*{{value_name}}, errors);
+ {% else %}
+ result->{{property.name | camelcase_to_hacker_style}}_ = internal::FromValue<{{resolve_type(property).raw_type}}>::Parse(*{{value_name}}, errors);
+ {% endif %}
+ {% if property.optional %}
+ }
+ {% else %}
+ } else {
+ errors->AddError("required property missing: {{property.name}}");
+ }
+ {% endif %}
+ {% endfor %}
+ errors->Pop();
+ errors->Pop();
+ if (errors->HasErrors())
+ return nullptr;
+ return result;
+}
+
+std::unique_ptr<base::Value> {{type.id}}::Serialize() const {
+ std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue());
+ {% for property in type.properties %}
+ {% set type = resolve_type(property) %}
+ {% if property.optional %}
+ if ({{property.name | camelcase_to_hacker_style}}_)
+ result->Set("{{property.name}}", internal::ToValue({{type.to_raw_type % ("%s_.value()" % property.name | camelcase_to_hacker_style)}}));
+ {% else %}
+ result->Set("{{property.name}}", internal::ToValue({{type.to_raw_type % ("%s_" % property.name | camelcase_to_hacker_style)}}));
+ {% endif %}
+ {% endfor %}
+ return std::move(result);
+}
+
+std::unique_ptr<{{type.id}}> {{type.id}}::Clone() const {
+ ErrorReporter errors;
+ std::unique_ptr<{{type.id}}> result = Parse(*Serialize(), &errors);
+ DCHECK(!errors.HasErrors());
+ return result;
+}
+
+ {% endfor %}
+} // namespace {{domain.domain | camelcase_to_hacker_style}}
+{% endfor %}
+
+} // namespace headless
diff --git a/chromium/headless/lib/browser/types_h.template b/chromium/headless/lib/browser/types_h.template
new file mode 100644
index 00000000000..25b341d89dd
--- /dev/null
+++ b/chromium/headless/lib/browser/types_h.template
@@ -0,0 +1,158 @@
+// This file is generated
+
+// Copyright (c) 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_PUBLIC_DOMAINS_TYPES_H_
+#define HEADLESS_PUBLIC_DOMAINS_TYPES_H_
+
+#include "base/optional.h"
+#include "base/values.h"
+#include "headless/public/headless_export.h"
+#include "headless/public/util/error_reporter.h"
+
+#include "base/memory/ptr_util.h"
+
+namespace headless {
+
+// ------------- Forward declarations and typedefs.
+
+{% for domain in api.domains %}
+
+namespace {{domain.domain | camelcase_to_hacker_style}} {
+ {% for type in domain.types %}
+ {% if type.type == "object" %}
+ {% if "properties" in type %}
+class {{type.id}};
+ {% else %}
+using {{type.id}} = base::Value;
+ {% endif %}
+ {% endif %}
+ {% endfor %}
+} // namespace {{domain.domain}}
+{% endfor %}
+
+{% for domain in api.domains %}
+namespace {{domain.domain | camelcase_to_hacker_style}} {
+ {% for type in domain.types %}
+ {% if "enum" in type %}
+enum class {{type.id}} {
+ {% for literal in type.enum %}
+ {{ literal | dash_to_camelcase | camelcase_to_hacker_style | upper | mangle_enum}}{{',' if not loop.last}}
+ {% endfor %}
+};
+
+ {% endif %}
+ {% endfor %}
+} // namespace {{domain.domain | camelcase_to_hacker_style}}
+
+{% endfor %}
+
+// ------------- Type and builder declarations.
+{% for domain in api.domains %}
+
+namespace {{domain.domain | camelcase_to_hacker_style}} {
+ {% for type in domain.types %}
+ {% if not (type.type == "object") or not ("properties" in type) %}{% continue %}{% endif %}
+
+ {% if type.description %}
+// {{type.description}}
+ {% endif %}
+class HEADLESS_EXPORT {{type.id}} {
+ public:
+ static {{resolve_type(type).pass_type}} Parse(const base::Value& value, ErrorReporter* errors);
+ ~{{type.id}}() { }
+ {% for property in type.properties %}
+
+ {% if property.description %}
+ // {{property.description}}
+ {% endif %}
+ {% if property.optional %}
+ bool Has{{property.name | to_title_case}}() const { return !!{{property.name | camelcase_to_hacker_style}}_; }
+ {{resolve_type(property).raw_return_type}} Get{{property.name | to_title_case}}() const { return {{resolve_type(property).to_raw_return_type % ("%s_.value()" % property.name | camelcase_to_hacker_style)}}; }
+ void Set{{property.name | to_title_case}}({{resolve_type(property).pass_type}} value) { {{property.name | camelcase_to_hacker_style}}_ = {{resolve_type(property).to_pass_type % 'value'}}; }
+ {% else %}
+ {{resolve_type(property).raw_return_type}} Get{{property.name | to_title_case}}() const { return {{resolve_type(property).to_raw_return_type % ("%s_" % property.name | camelcase_to_hacker_style)}}; }
+ void Set{{property.name | to_title_case}}({{resolve_type(property).pass_type}} value) { {{property.name | camelcase_to_hacker_style}}_ = {{resolve_type(property).to_pass_type % 'value'}}; }
+ {% endif %}
+ {% endfor %}
+
+ std::unique_ptr<base::Value> Serialize() const;
+ {{resolve_type(type).pass_type}} Clone() const;
+
+ template<int STATE>
+ class {{type.id}}Builder {
+ public:
+ enum {
+ kNoFieldsSet = 0,
+ {% set count = 0 %}
+ {% for property in type.properties %}
+ {% if not(property.optional) %}
+ {% set count = count + 1 %}
+ k{{property.name | to_title_case}}Set = 1 << {{count}},
+ {% endif %}
+ {% endfor %}
+ kAllRequiredFieldsSet = (
+ {%- for property in type.properties %}
+ {% if not(property.optional) %}k{{property.name | to_title_case}}Set | {%endif %}
+ {% endfor %}0)
+ };
+
+ {% for property in type.properties %}
+ {% if property.optional %}
+ {{type.id}}Builder<STATE>& Set{{property.name | to_title_case}}({{resolve_type(property).pass_type}} value) {
+ result_->Set{{property.name | to_title_case}}({{resolve_type(property).to_pass_type % 'value'}});
+ return *this;
+ }
+ {% else %}
+ {{type.id}}Builder<STATE | k{{property.name | to_title_case}}Set>& Set{{property.name | to_title_case}}({{resolve_type(property).pass_type}} value) {
+ static_assert(!(STATE & k{{property.name | to_title_case}}Set), "property {{property.name}} should not have already been set");
+ result_->Set{{property.name | to_title_case}}({{resolve_type(property).to_pass_type % 'value'}});
+ return CastState<k{{property.name | to_title_case}}Set>();
+ }
+ {% endif %}
+
+ {% endfor %}
+ {{resolve_type(type).pass_type}} Build() {
+ static_assert(STATE == kAllRequiredFieldsSet, "all required fields should have been set");
+ return std::move(result_);
+ }
+
+ private:
+ friend class {{type.id}};
+ {{type.id}}Builder() : result_(new {{type.id}}()) { }
+
+ template<int STEP> {{type.id}}Builder<STATE | STEP>& CastState() {
+ return *reinterpret_cast<{{type.id}}Builder<STATE | STEP>*>(this);
+ }
+
+ {{resolve_type(type).type}} result_;
+ };
+
+ static {{type.id}}Builder<0> Builder() {
+ return {{type.id}}Builder<0>();
+ }
+
+ private:
+ {{type.id}}() { }
+
+ {% for property in type.properties %}
+ {% if property.optional %}
+ base::Optional<{{resolve_type(property).type}}> {{property.name | camelcase_to_hacker_style}}_;
+ {% else %}
+ {{resolve_type(property).type}} {{property.name | camelcase_to_hacker_style}}_;
+ {% endif %}
+ {% endfor %}
+
+ DISALLOW_COPY_AND_ASSIGN({{type.id}});
+};
+
+ {% endfor %}
+
+} // namespace {{domain.domain | camelcase_to_hacker_style}}
+{% endfor %}
+
+} // namespace headless
+
+#endif // HEADLESS_PUBLIC_DOMAINS_TYPES_H_
diff --git a/chromium/headless/lib/headless_browser_browsertest.cc b/chromium/headless/lib/headless_browser_browsertest.cc
index 9cc594073ce..d6eb82b21e9 100644
--- a/chromium/headless/lib/headless_browser_browsertest.cc
+++ b/chromium/headless/lib/headless_browser_browsertest.cc
@@ -4,6 +4,7 @@
#include <memory>
+#include "base/strings/stringprintf.h"
#include "content/public/test/browser_test.h"
#include "headless/public/headless_browser.h"
#include "headless/public/headless_web_contents.h"
@@ -15,18 +16,24 @@
namespace headless {
IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, CreateAndDestroyWebContents) {
- std::unique_ptr<HeadlessWebContents> web_contents =
+ HeadlessWebContents* web_contents =
browser()->CreateWebContents(GURL("about:blank"), gfx::Size(800, 600));
EXPECT_TRUE(web_contents);
+
+ EXPECT_EQ(static_cast<size_t>(1), browser()->GetAllWebContents().size());
+ EXPECT_EQ(web_contents, browser()->GetAllWebContents()[0]);
// TODO(skyostil): Verify viewport dimensions once we can.
- web_contents.reset();
+ web_contents->Close();
+
+ EXPECT_TRUE(browser()->GetAllWebContents().empty());
}
IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, CreateWithBadURL) {
GURL bad_url("not_valid");
- std::unique_ptr<HeadlessWebContents> web_contents =
+ HeadlessWebContents* web_contents =
browser()->CreateWebContents(bad_url, gfx::Size(800, 600));
EXPECT_FALSE(web_contents);
+ EXPECT_TRUE(browser()->GetAllWebContents().empty());
}
class HeadlessBrowserTestWithProxy : public HeadlessBrowserTest {
@@ -60,11 +67,31 @@ IN_PROC_BROWSER_TEST_F(HeadlessBrowserTestWithProxy, SetProxyServer) {
// Load a page which doesn't actually exist, but for which the our proxy
// returns valid content anyway.
- std::unique_ptr<HeadlessWebContents> web_contents =
- browser()->CreateWebContents(
- GURL("http://not-an-actual-domain.tld/hello.html"),
- gfx::Size(800, 600));
- EXPECT_TRUE(WaitForLoad(web_contents.get()));
+ //
+ // TODO(altimin): Currently this construction does not serve hello.html
+ // from headless/test/data as expected. We should fix this.
+ HeadlessWebContents* web_contents = browser()->CreateWebContents(
+ GURL("http://not-an-actual-domain.tld/hello.html"), gfx::Size(800, 600));
+ EXPECT_TRUE(WaitForLoad(web_contents));
+ EXPECT_EQ(static_cast<size_t>(1), browser()->GetAllWebContents().size());
+ EXPECT_EQ(web_contents, browser()->GetAllWebContents()[0]);
+ web_contents->Close();
+ EXPECT_TRUE(browser()->GetAllWebContents().empty());
+}
+
+IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, SetHostResolverRules) {
+ EXPECT_TRUE(embedded_test_server()->Start());
+ HeadlessBrowser::Options::Builder builder;
+ builder.SetHostResolverRules(
+ base::StringPrintf("MAP not-an-actual-domain.tld 127.0.0.1:%d",
+ embedded_test_server()->host_port_pair().port()));
+ SetBrowserOptions(builder.Build());
+
+ // Load a page which doesn't actually exist, but which is turned into a valid
+ // address by our host resolver rules.
+ HeadlessWebContents* web_contents = browser()->CreateWebContents(
+ GURL("http://not-an-actual-domain.tld/hello.html"), gfx::Size(800, 600));
+ EXPECT_TRUE(WaitForLoad(web_contents));
}
} // namespace headless
diff --git a/chromium/headless/lib/headless_content_main_delegate.cc b/chromium/headless/lib/headless_content_main_delegate.cc
index 245341fcb43..e7ea8d7690d 100644
--- a/chromium/headless/lib/headless_content_main_delegate.cc
+++ b/chromium/headless/lib/headless_content_main_delegate.cc
@@ -41,8 +41,8 @@ HeadlessContentMainDelegate::~HeadlessContentMainDelegate() {
bool HeadlessContentMainDelegate::BasicStartupComplete(int* exit_code) {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
- command_line->AppendSwitch(switches::kNoSandbox);
- command_line->AppendSwitch(switches::kSingleProcess);
+ if (browser_->options().single_process_mode)
+ command_line->AppendSwitch(switches::kSingleProcess);
// 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.
diff --git a/chromium/headless/lib/headless_devtools_client_browsertest.cc b/chromium/headless/lib/headless_devtools_client_browsertest.cc
new file mode 100644
index 00000000000..5e073a8f31e
--- /dev/null
+++ b/chromium/headless/lib/headless_devtools_client_browsertest.cc
@@ -0,0 +1,213 @@
+// 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 <memory>
+
+#include "content/public/test/browser_test.h"
+#include "headless/public/domains/network.h"
+#include "headless/public/domains/page.h"
+#include "headless/public/domains/runtime.h"
+#include "headless/public/headless_browser.h"
+#include "headless/public/headless_devtools_client.h"
+#include "headless/public/headless_devtools_target.h"
+#include "headless/public/headless_web_contents.h"
+#include "headless/test/headless_browser_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/size.h"
+#include "url/gurl.h"
+
+namespace headless {
+
+#define DEVTOOLS_CLIENT_TEST_F(TEST_FIXTURE_NAME) \
+ IN_PROC_BROWSER_TEST_F(TEST_FIXTURE_NAME, RunAsyncTest) { RunTest(); } \
+ class AsyncHeadlessBrowserTestNeedsSemicolon##TEST_FIXTURE_NAME {}
+
+// A test fixture which attaches a devtools client before starting the test.
+class HeadlessDevToolsClientTest : public HeadlessBrowserTest,
+ public HeadlessWebContents::Observer {
+ public:
+ HeadlessDevToolsClientTest()
+ : devtools_client_(HeadlessDevToolsClient::Create()) {}
+ ~HeadlessDevToolsClientTest() override {}
+
+ // HeadlessWebContentsObserver implementation:
+ void DevToolsTargetReady() override {
+ EXPECT_TRUE(web_contents_->GetDevToolsTarget());
+ web_contents_->GetDevToolsTarget()->AttachClient(devtools_client_.get());
+ RunDevToolsClientTest();
+ }
+
+ virtual void RunDevToolsClientTest() = 0;
+
+ protected:
+ void RunTest() {
+ web_contents_ =
+ browser()->CreateWebContents(GURL("about:blank"), gfx::Size(800, 600));
+ web_contents_->AddObserver(this);
+
+ RunAsynchronousTest();
+
+ web_contents_->GetDevToolsTarget()->DetachClient(devtools_client_.get());
+ web_contents_->RemoveObserver(this);
+ web_contents_->Close();
+ web_contents_ = nullptr;
+ }
+
+ HeadlessWebContents* web_contents_;
+ std::unique_ptr<HeadlessDevToolsClient> devtools_client_;
+};
+
+class HeadlessDevToolsClientNavigationTest : public HeadlessDevToolsClientTest,
+ page::ExperimentalObserver {
+ public:
+ void RunDevToolsClientTest() override {
+ EXPECT_TRUE(embedded_test_server()->Start());
+ std::unique_ptr<page::NavigateParams> params =
+ page::NavigateParams::Builder()
+ .SetUrl(embedded_test_server()->GetURL("/hello.html").spec())
+ .Build();
+ devtools_client_->GetPage()->GetExperimental()->AddObserver(this);
+ devtools_client_->GetPage()->Enable();
+ devtools_client_->GetPage()->Navigate(std::move(params));
+ }
+
+ void OnLoadEventFired(const page::LoadEventFiredParams& params) override {
+ devtools_client_->GetPage()->GetExperimental()->RemoveObserver(this);
+ FinishAsynchronousTest();
+ }
+
+ // Check that events with no parameters still get a parameters object.
+ void OnFrameResized(const page::FrameResizedParams& params) override {}
+};
+
+DEVTOOLS_CLIENT_TEST_F(HeadlessDevToolsClientNavigationTest);
+
+class HeadlessDevToolsClientEvalTest : public HeadlessDevToolsClientTest {
+ public:
+ void RunDevToolsClientTest() override {
+ std::unique_ptr<runtime::EvaluateParams> params =
+ runtime::EvaluateParams::Builder().SetExpression("1 + 2").Build();
+ devtools_client_->GetRuntime()->Evaluate(
+ std::move(params),
+ base::Bind(&HeadlessDevToolsClientEvalTest::OnFirstResult,
+ base::Unretained(this)));
+ // Test the convenience overload which only takes the required command
+ // parameters.
+ devtools_client_->GetRuntime()->Evaluate(
+ "24 * 7", base::Bind(&HeadlessDevToolsClientEvalTest::OnSecondResult,
+ 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(3, value);
+ }
+
+ void OnSecondResult(std::unique_ptr<runtime::EvaluateResult> result) {
+ int value;
+ EXPECT_TRUE(result->GetResult()->HasValue());
+ EXPECT_TRUE(result->GetResult()->GetValue()->GetAsInteger(&value));
+ EXPECT_EQ(168, value);
+ FinishAsynchronousTest();
+ }
+};
+
+DEVTOOLS_CLIENT_TEST_F(HeadlessDevToolsClientEvalTest);
+
+class HeadlessDevToolsClientCallbackTest : public HeadlessDevToolsClientTest {
+ public:
+ HeadlessDevToolsClientCallbackTest() : first_result_received_(false) {}
+
+ void RunDevToolsClientTest() override {
+ // Null callback without parameters.
+ devtools_client_->GetPage()->Enable();
+ // Null callback with parameters.
+ devtools_client_->GetRuntime()->Evaluate("true");
+ // Non-null callback without parameters.
+ devtools_client_->GetPage()->Disable(
+ base::Bind(&HeadlessDevToolsClientCallbackTest::OnFirstResult,
+ base::Unretained(this)));
+ // Non-null callback with parameters.
+ devtools_client_->GetRuntime()->Evaluate(
+ "true", base::Bind(&HeadlessDevToolsClientCallbackTest::OnSecondResult,
+ base::Unretained(this)));
+ }
+
+ void OnFirstResult() {
+ EXPECT_FALSE(first_result_received_);
+ first_result_received_ = true;
+ }
+
+ void OnSecondResult(std::unique_ptr<runtime::EvaluateResult> result) {
+ EXPECT_TRUE(first_result_received_);
+ FinishAsynchronousTest();
+ }
+
+ private:
+ bool first_result_received_;
+};
+
+DEVTOOLS_CLIENT_TEST_F(HeadlessDevToolsClientCallbackTest);
+
+class HeadlessDevToolsClientObserverTest : public HeadlessDevToolsClientTest,
+ network::Observer {
+ public:
+ void RunDevToolsClientTest() override {
+ EXPECT_TRUE(embedded_test_server()->Start());
+ devtools_client_->GetNetwork()->AddObserver(this);
+ devtools_client_->GetNetwork()->Enable();
+ devtools_client_->GetPage()->Navigate(
+ embedded_test_server()->GetURL("/hello.html").spec());
+ }
+
+ void OnRequestWillBeSent(
+ const network::RequestWillBeSentParams& params) override {
+ EXPECT_EQ("GET", params.GetRequest()->GetMethod());
+ EXPECT_EQ(embedded_test_server()->GetURL("/hello.html").spec(),
+ params.GetRequest()->GetUrl());
+ }
+
+ void OnResponseReceived(
+ const network::ResponseReceivedParams& params) override {
+ EXPECT_EQ(200, params.GetResponse()->GetStatus());
+ EXPECT_EQ("OK", params.GetResponse()->GetStatusText());
+ std::string content_type;
+ EXPECT_TRUE(params.GetResponse()->GetHeaders()->GetString("Content-Type",
+ &content_type));
+ EXPECT_EQ("text/html", content_type);
+
+ devtools_client_->GetNetwork()->RemoveObserver(this);
+ FinishAsynchronousTest();
+ }
+};
+
+DEVTOOLS_CLIENT_TEST_F(HeadlessDevToolsClientObserverTest);
+
+class HeadlessDevToolsClientExperimentalTest
+ : public HeadlessDevToolsClientTest,
+ page::ExperimentalObserver {
+ public:
+ void RunDevToolsClientTest() override {
+ EXPECT_TRUE(embedded_test_server()->Start());
+ // Check that experimental commands require parameter objects.
+ devtools_client_->GetRuntime()->GetExperimental()->Run(
+ runtime::RunParams::Builder().Build());
+
+ devtools_client_->GetPage()->GetExperimental()->AddObserver(this);
+ devtools_client_->GetPage()->Enable();
+ devtools_client_->GetPage()->Navigate(
+ embedded_test_server()->GetURL("/hello.html").spec());
+ }
+
+ void OnFrameStoppedLoading(
+ const page::FrameStoppedLoadingParams& params) override {
+ FinishAsynchronousTest();
+ }
+};
+
+DEVTOOLS_CLIENT_TEST_F(HeadlessDevToolsClientExperimentalTest);
+
+} // namespace headless
diff --git a/chromium/headless/lib/headless_web_contents_browsertest.cc b/chromium/headless/lib/headless_web_contents_browsertest.cc
index aaa5491882b..30faee5e65e 100644
--- a/chromium/headless/lib/headless_web_contents_browsertest.cc
+++ b/chromium/headless/lib/headless_web_contents_browsertest.cc
@@ -16,39 +16,30 @@ namespace headless {
class HeadlessWebContentsTest : public HeadlessBrowserTest {};
-class NavigationObserver : public HeadlessWebContents::Observer {
- public:
- NavigationObserver(HeadlessWebContentsTest* browser_test)
- : browser_test_(browser_test), navigation_succeeded_(false) {}
- ~NavigationObserver() override {}
-
- void DocumentOnLoadCompletedInMainFrame() override {
- browser_test_->FinishAsynchronousTest();
- }
-
- void DidFinishNavigation(bool success) override {
- navigation_succeeded_ = success;
- }
+IN_PROC_BROWSER_TEST_F(HeadlessWebContentsTest, Navigation) {
+ EXPECT_TRUE(embedded_test_server()->Start());
+ HeadlessWebContents* web_contents = browser()->CreateWebContents(
+ embedded_test_server()->GetURL("/hello.html"), gfx::Size(800, 600));
+ EXPECT_TRUE(WaitForLoad(web_contents));
- bool navigation_succeeded() const { return navigation_succeeded_; }
+ std::vector<HeadlessWebContents*> all_web_contents =
+ browser()->GetAllWebContents();
- private:
- HeadlessWebContentsTest* browser_test_; // Not owned.
- bool navigation_succeeded_;
-};
+ EXPECT_EQ(static_cast<size_t>(1), all_web_contents.size());
+ EXPECT_EQ(web_contents, all_web_contents[0]);
+}
-IN_PROC_BROWSER_TEST_F(HeadlessWebContentsTest, Navigation) {
+IN_PROC_BROWSER_TEST_F(HeadlessWebContentsTest, WindowOpen) {
EXPECT_TRUE(embedded_test_server()->Start());
- std::unique_ptr<HeadlessWebContents> web_contents =
- browser()->CreateWebContents(
- embedded_test_server()->GetURL("/hello.html"), gfx::Size(800, 600));
- NavigationObserver observer(this);
- web_contents->AddObserver(&observer);
- RunAsynchronousTest();
+ HeadlessWebContents* web_contents = browser()->CreateWebContents(
+ embedded_test_server()->GetURL("/window_open.html"), gfx::Size(800, 600));
+ EXPECT_TRUE(WaitForLoad(web_contents));
+
+ std::vector<HeadlessWebContents*> all_web_contents =
+ browser()->GetAllWebContents();
- EXPECT_TRUE(observer.navigation_succeeded());
- web_contents->RemoveObserver(&observer);
+ EXPECT_EQ(static_cast<size_t>(2), all_web_contents.size());
}
} // namespace headless
diff --git a/chromium/headless/public/domains/README.md b/chromium/headless/public/domains/README.md
new file mode 100644
index 00000000000..a3f8898c5b6
--- /dev/null
+++ b/chromium/headless/public/domains/README.md
@@ -0,0 +1,3 @@
+The client API domain classes are autogenerated. You can find them under the
+out-directory, e.g., out/Debug/gen/headless/public/domains, or in
+[Code Search](https://code.google.com/p/chromium/codesearch#search/&q=f%3Agen/headless/public/domains&sq=package:chromium&type=cs).
diff --git a/chromium/headless/public/domains/types_unittest.cc b/chromium/headless/public/domains/types_unittest.cc
new file mode 100644
index 00000000000..c665d95b225
--- /dev/null
+++ b/chromium/headless/public/domains/types_unittest.cc
@@ -0,0 +1,200 @@
+// 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 "base/json/json_reader.h"
+#include "headless/public/domains/types.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace headless {
+
+TEST(TypesTest, IntegerProperty) {
+ std::unique_ptr<accessibility::GetAXNodeParams> object(
+ accessibility::GetAXNodeParams::Builder().SetNodeId(123).Build());
+ EXPECT_TRUE(object);
+ EXPECT_EQ(123, object->GetNodeId());
+
+ std::unique_ptr<accessibility::GetAXNodeParams> clone(object->Clone());
+ EXPECT_TRUE(clone);
+ EXPECT_EQ(123, clone->GetNodeId());
+}
+
+TEST(TypesTest, IntegerPropertyParseError) {
+ const char* json = "{\"nodeId\": \"foo\"}";
+ std::unique_ptr<base::Value> object = base::JSONReader::Read(json);
+ EXPECT_TRUE(object);
+
+ ErrorReporter errors;
+ EXPECT_FALSE(accessibility::GetAXNodeParams::Parse(*object, &errors));
+ EXPECT_TRUE(errors.HasErrors());
+}
+
+TEST(TypesTest, BooleanProperty) {
+ std::unique_ptr<memory::SetPressureNotificationsSuppressedParams> object(
+ memory::SetPressureNotificationsSuppressedParams::Builder()
+ .SetSuppressed(true)
+ .Build());
+ EXPECT_TRUE(object);
+ EXPECT_TRUE(object->GetSuppressed());
+
+ std::unique_ptr<memory::SetPressureNotificationsSuppressedParams> clone(
+ object->Clone());
+ EXPECT_TRUE(clone);
+ EXPECT_TRUE(clone->GetSuppressed());
+}
+
+TEST(TypesTest, BooleanPropertyParseError) {
+ const char* json = "{\"suppressed\": \"foo\"}";
+ std::unique_ptr<base::Value> object = base::JSONReader::Read(json);
+ EXPECT_TRUE(object);
+
+ ErrorReporter errors;
+ EXPECT_FALSE(memory::SetPressureNotificationsSuppressedParams::Parse(
+ *object, &errors));
+ EXPECT_TRUE(errors.HasErrors());
+}
+
+TEST(TypesTest, DoubleProperty) {
+ std::unique_ptr<page::SetGeolocationOverrideParams> object(
+ page::SetGeolocationOverrideParams::Builder().SetLatitude(3.14).Build());
+ EXPECT_TRUE(object);
+ EXPECT_EQ(3.14, object->GetLatitude());
+
+ std::unique_ptr<page::SetGeolocationOverrideParams> clone(object->Clone());
+ EXPECT_TRUE(clone);
+ EXPECT_EQ(3.14, clone->GetLatitude());
+}
+
+TEST(TypesTest, DoublePropertyParseError) {
+ const char* json = "{\"latitude\": \"foo\"}";
+ std::unique_ptr<base::Value> object = base::JSONReader::Read(json);
+ EXPECT_TRUE(object);
+
+ ErrorReporter errors;
+ EXPECT_FALSE(page::SetGeolocationOverrideParams::Parse(*object, &errors));
+ EXPECT_TRUE(errors.HasErrors());
+}
+
+TEST(TypesTest, StringProperty) {
+ std::unique_ptr<page::NavigateParams> object(
+ page::NavigateParams::Builder().SetUrl("url").Build());
+ EXPECT_TRUE(object);
+ EXPECT_EQ("url", object->GetUrl());
+
+ std::unique_ptr<page::NavigateParams> clone(object->Clone());
+ EXPECT_TRUE(clone);
+ EXPECT_EQ("url", clone->GetUrl());
+}
+
+TEST(TypesTest, StringPropertyParseError) {
+ const char* json = "{\"url\": false}";
+ std::unique_ptr<base::Value> object = base::JSONReader::Read(json);
+ EXPECT_TRUE(object);
+
+ ErrorReporter errors;
+ EXPECT_FALSE(page::NavigateParams::Parse(*object, &errors));
+ EXPECT_TRUE(errors.HasErrors());
+}
+
+TEST(TypesTest, EnumProperty) {
+ std::unique_ptr<runtime::RemoteObject> object(
+ runtime::RemoteObject::Builder()
+ .SetType(runtime::RemoteObjectType::UNDEFINED)
+ .Build());
+ EXPECT_TRUE(object);
+ EXPECT_EQ(runtime::RemoteObjectType::UNDEFINED, object->GetType());
+
+ std::unique_ptr<runtime::RemoteObject> clone(object->Clone());
+ EXPECT_TRUE(clone);
+ EXPECT_EQ(runtime::RemoteObjectType::UNDEFINED, clone->GetType());
+}
+
+TEST(TypesTest, EnumPropertyParseError) {
+ const char* json = "{\"type\": false}";
+ std::unique_ptr<base::Value> object = base::JSONReader::Read(json);
+ EXPECT_TRUE(object);
+
+ ErrorReporter errors;
+ EXPECT_FALSE(runtime::RemoteObject::Parse(*object, &errors));
+ EXPECT_TRUE(errors.HasErrors());
+}
+
+TEST(TypesTest, ArrayProperty) {
+ std::vector<int> values;
+ values.push_back(1);
+ values.push_back(2);
+ values.push_back(3);
+
+ std::unique_ptr<dom::QuerySelectorAllResult> object(
+ dom::QuerySelectorAllResult::Builder().SetNodeIds(values).Build());
+ EXPECT_TRUE(object);
+ EXPECT_EQ(3u, object->GetNodeIds()->size());
+ EXPECT_EQ(1, object->GetNodeIds()->at(0));
+ EXPECT_EQ(2, object->GetNodeIds()->at(1));
+ EXPECT_EQ(3, object->GetNodeIds()->at(2));
+
+ std::unique_ptr<dom::QuerySelectorAllResult> clone(object->Clone());
+ EXPECT_TRUE(clone);
+ EXPECT_EQ(3u, clone->GetNodeIds()->size());
+ EXPECT_EQ(1, clone->GetNodeIds()->at(0));
+ EXPECT_EQ(2, clone->GetNodeIds()->at(1));
+ EXPECT_EQ(3, clone->GetNodeIds()->at(2));
+}
+
+TEST(TypesTest, ArrayPropertyParseError) {
+ const char* json = "{\"nodeIds\": true}";
+ std::unique_ptr<base::Value> object = base::JSONReader::Read(json);
+ EXPECT_TRUE(object);
+
+ ErrorReporter errors;
+ EXPECT_FALSE(dom::QuerySelectorAllResult::Parse(*object, &errors));
+ EXPECT_TRUE(errors.HasErrors());
+}
+
+TEST(TypesTest, ObjectProperty) {
+ std::unique_ptr<runtime::RemoteObject> subobject(
+ runtime::RemoteObject::Builder()
+ .SetType(runtime::RemoteObjectType::SYMBOL)
+ .Build());
+ std::unique_ptr<runtime::EvaluateResult> object(
+ runtime::EvaluateResult::Builder()
+ .SetResult(std::move(subobject))
+ .Build());
+ EXPECT_TRUE(object);
+ EXPECT_EQ(runtime::RemoteObjectType::SYMBOL, object->GetResult()->GetType());
+
+ std::unique_ptr<runtime::EvaluateResult> clone(object->Clone());
+ EXPECT_TRUE(clone);
+ EXPECT_EQ(runtime::RemoteObjectType::SYMBOL, clone->GetResult()->GetType());
+}
+
+TEST(TypesTest, ObjectPropertyParseError) {
+ const char* json = "{\"result\": 42}";
+ std::unique_ptr<base::Value> object = base::JSONReader::Read(json);
+ EXPECT_TRUE(object);
+
+ ErrorReporter errors;
+ EXPECT_FALSE(runtime::EvaluateResult::Parse(*object, &errors));
+ EXPECT_TRUE(errors.HasErrors());
+}
+
+TEST(TypesTest, AnyProperty) {
+ std::unique_ptr<base::Value> value(new base::FundamentalValue(123));
+ std::unique_ptr<accessibility::AXValue> object(
+ accessibility::AXValue::Builder()
+ .SetType(accessibility::AXValueType::INTEGER)
+ .SetValue(std::move(value))
+ .Build());
+ EXPECT_TRUE(object);
+ 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());
+
+ int clone_value;
+ EXPECT_TRUE(clone->GetValue()->GetAsInteger(&clone_value));
+ EXPECT_EQ(123, clone_value);
+}
+
+} // namespace headless
diff --git a/chromium/headless/public/headless_browser.cc b/chromium/headless/public/headless_browser.cc
index b66efa40724..6ff0a0c35ff 100644
--- a/chromium/headless/public/headless_browser.cc
+++ b/chromium/headless/public/headless_browser.cc
@@ -20,7 +20,8 @@ Options::Options(int argc, const char** argv)
: argc(argc),
argv(argv),
user_agent(content::BuildUserAgentFromProduct(kProductName)),
- message_pump(nullptr) {}
+ message_pump(nullptr),
+ single_process_mode(false) {}
Options::Options(const Options& other) = default;
@@ -52,6 +53,16 @@ Builder& Builder::SetProxyServer(const net::HostPortPair& proxy_server) {
return *this;
}
+Builder& Builder::SetHostResolverRules(const std::string& host_resolver_rules) {
+ options_.host_resolver_rules = host_resolver_rules;
+ return *this;
+}
+
+Builder& Builder::SetSingleProcessMode(bool single_process_mode) {
+ options_.single_process_mode = single_process_mode;
+ return *this;
+}
+
Options Builder::Build() {
return options_;
}
diff --git a/chromium/headless/public/headless_browser.h b/chromium/headless/public/headless_browser.h
index 011017471e1..de5f89bd3ef 100644
--- a/chromium/headless/public/headless_browser.h
+++ b/chromium/headless/public/headless_browser.h
@@ -37,9 +37,13 @@ class HEADLESS_EXPORT HeadlessBrowser {
// Create a new browser tab which navigates to |initial_url|. |size| is in
// physical pixels.
- virtual std::unique_ptr<HeadlessWebContents> CreateWebContents(
- const GURL& initial_url,
- const gfx::Size& size) = 0;
+ // We require the user to pass an initial URL to ensure that the renderer
+ // gets initialized and eventually becomes ready to be inspected. See
+ // HeadlessWebContents::Observer::DevToolsTargetReady.
+ virtual HeadlessWebContents* CreateWebContents(const GURL& initial_url,
+ const gfx::Size& size) = 0;
+
+ virtual std::vector<HeadlessWebContents*> GetAllWebContents() = 0;
// Returns a task runner for submitting work to the browser main thread.
virtual scoped_refptr<base::SingleThreadTaskRunner> BrowserMainThread()
@@ -82,6 +86,15 @@ struct HeadlessBrowser::Options {
// Optional message pump that overrides the default. Must outlive the browser.
base::MessagePump* message_pump;
+ // Comma-separated list of rules that control how hostnames are mapped. See
+ // chrome::switches::kHostRules for a description for the format.
+ std::string host_resolver_rules;
+
+ // Run the browser in single process mode instead of using separate renderer
+ // processes as per default. Note that this also disables any sandboxing of
+ // web content, which can be a security risk.
+ bool single_process_mode;
+
private:
Options(int argc, const char** argv);
};
@@ -96,6 +109,8 @@ class HeadlessBrowser::Options::Builder {
Builder& EnableDevToolsServer(const net::IPEndPoint& endpoint);
Builder& SetMessagePump(base::MessagePump* message_pump);
Builder& SetProxyServer(const net::HostPortPair& proxy_server);
+ Builder& SetHostResolverRules(const std::string& host_resolver_rules);
+ Builder& SetSingleProcessMode(bool single_process_mode);
Options Build();
diff --git a/chromium/headless/public/headless_devtools_client.h b/chromium/headless/public/headless_devtools_client.h
new file mode 100644
index 00000000000..a71ce657200
--- /dev/null
+++ b/chromium/headless/public/headless_devtools_client.h
@@ -0,0 +1,157 @@
+// 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_PUBLIC_HEADLESS_DEVTOOLS_CLIENT_H_
+#define HEADLESS_PUBLIC_HEADLESS_DEVTOOLS_CLIENT_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "headless/public/headless_export.h"
+
+namespace headless {
+
+namespace accessibility {
+class Domain;
+}
+namespace animation {
+class Domain;
+}
+namespace application_cache {
+class Domain;
+}
+namespace cache_storage {
+class Domain;
+}
+namespace console {
+class Domain;
+}
+namespace css {
+class Domain;
+}
+namespace database {
+class Domain;
+}
+namespace debugger {
+class Domain;
+}
+namespace device_orientation {
+class Domain;
+}
+namespace dom {
+class Domain;
+}
+namespace dom_debugger {
+class Domain;
+}
+namespace dom_storage {
+class Domain;
+}
+namespace emulation {
+class Domain;
+}
+namespace heap_profiler {
+class Domain;
+}
+namespace indexeddb {
+class Domain;
+}
+namespace input {
+class Domain;
+}
+namespace inspector {
+class Domain;
+}
+namespace io {
+class Domain;
+}
+namespace layer_tree {
+class Domain;
+}
+namespace memory {
+class Domain;
+}
+namespace network {
+class Domain;
+}
+namespace page {
+class Domain;
+}
+namespace profiler {
+class Domain;
+}
+namespace rendering {
+class Domain;
+}
+namespace runtime {
+class Domain;
+}
+namespace security {
+class Domain;
+}
+namespace service_worker {
+class Domain;
+}
+namespace tracing {
+class Domain;
+}
+namespace worker {
+class Domain;
+}
+
+// An interface for controlling and receiving events from a devtools target.
+class HEADLESS_EXPORT HeadlessDevToolsClient {
+ public:
+ virtual ~HeadlessDevToolsClient() {}
+
+ static std::unique_ptr<HeadlessDevToolsClient> Create();
+
+ // DevTools commands are split into domains which corresponds to the getters
+ // below. Each domain can be used to send commands and to subscribe to events.
+ //
+ // See http://chromedevtools.github.io/debugger-protocol-viewer/ for
+ // the capabilities of each domain.
+ virtual accessibility::Domain* GetAccessibility() = 0;
+ virtual animation::Domain* GetAnimation() = 0;
+ virtual application_cache::Domain* GetApplicationCache() = 0;
+ virtual cache_storage::Domain* GetCacheStorage() = 0;
+ virtual console::Domain* GetConsole() = 0;
+ virtual css::Domain* GetCSS() = 0;
+ virtual database::Domain* GetDatabase() = 0;
+ virtual debugger::Domain* GetDebugger() = 0;
+ virtual device_orientation::Domain* GetDeviceOrientation() = 0;
+ virtual dom::Domain* GetDOM() = 0;
+ virtual dom_debugger::Domain* GetDOMDebugger() = 0;
+ virtual dom_storage::Domain* GetDOMStorage() = 0;
+ virtual emulation::Domain* GetEmulation() = 0;
+ virtual heap_profiler::Domain* GetHeapProfiler() = 0;
+ virtual indexeddb::Domain* GetIndexedDB() = 0;
+ virtual input::Domain* GetInput() = 0;
+ virtual inspector::Domain* GetInspector() = 0;
+ virtual io::Domain* GetIO() = 0;
+ virtual layer_tree::Domain* GetLayerTree() = 0;
+ virtual memory::Domain* GetMemory() = 0;
+ virtual network::Domain* GetNetwork() = 0;
+ virtual page::Domain* GetPage() = 0;
+ virtual profiler::Domain* GetProfiler() = 0;
+ virtual rendering::Domain* GetRendering() = 0;
+ virtual runtime::Domain* GetRuntime() = 0;
+ virtual security::Domain* GetSecurity() = 0;
+ virtual service_worker::Domain* GetServiceWorker() = 0;
+ virtual tracing::Domain* GetTracing() = 0;
+ virtual worker::Domain* GetWorker() = 0;
+
+ // TODO(skyostil): Add notification for disconnection.
+
+ private:
+ friend class HeadlessDevToolsClientImpl;
+
+ HeadlessDevToolsClient() {}
+
+ DISALLOW_COPY_AND_ASSIGN(HeadlessDevToolsClient);
+};
+
+} // namespace headless
+
+#endif // HEADLESS_PUBLIC_HEADLESS_DEVTOOLS_CLIENT_H_
diff --git a/chromium/headless/public/headless_devtools_target.h b/chromium/headless/public/headless_devtools_target.h
new file mode 100644
index 00000000000..941fe6f51e2
--- /dev/null
+++ b/chromium/headless/public/headless_devtools_target.h
@@ -0,0 +1,37 @@
+// 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_PUBLIC_HEADLESS_DEVTOOLS_TARGET_H_
+#define HEADLESS_PUBLIC_HEADLESS_DEVTOOLS_TARGET_H_
+
+#include "base/macros.h"
+#include "headless/public/headless_export.h"
+
+namespace headless {
+class HeadlessDevToolsClient;
+
+// A target which can be controlled and inspected using DevTools.
+class HEADLESS_EXPORT HeadlessDevToolsTarget {
+ public:
+ HeadlessDevToolsTarget() {}
+ virtual ~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.
+ //
+ // |client| must outlive this target.
+ virtual void AttachClient(HeadlessDevToolsClient* client) = 0;
+ virtual void DetachClient(HeadlessDevToolsClient* client) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HeadlessDevToolsTarget);
+};
+
+} // namespace headless
+
+#endif // HEADLESS_PUBLIC_HEADLESS_DEVTOOLS_TARGET_H_
diff --git a/chromium/headless/public/headless_web_contents.h b/chromium/headless/public/headless_web_contents.h
index 04ac953001d..27a4e8f4b12 100644
--- a/chromium/headless/public/headless_web_contents.h
+++ b/chromium/headless/public/headless_web_contents.h
@@ -7,11 +7,11 @@
#include "base/callback.h"
#include "base/macros.h"
-#include "base/memory/scoped_ptr.h"
#include "headless/public/headless_export.h"
#include "url/gurl.h"
namespace headless {
+class HeadlessDevToolsTarget;
// Class representing contents of a browser tab. Should be accessed from browser
// main thread.
@@ -19,16 +19,15 @@ class HEADLESS_EXPORT HeadlessWebContents {
public:
virtual ~HeadlessWebContents() {}
- // TODO(skyostil): Replace this with an equivalent client API.
class Observer {
public:
// All the following notifications will be called on browser main thread.
- virtual void DocumentOnLoadCompletedInMainFrame(){};
- virtual void DidFinishNavigation(bool success){};
- // After this event, this HeadlessWebContents instance is ready to be
- // controlled using a DevTools client.
- virtual void WebContentsReady(){};
+ // Indicates that this HeadlessWebContents instance is now ready to be
+ // inspected using a HeadlessDevToolsClient.
+ //
+ // TODO(altimin): Support this event for pages that aren't created by us.
+ virtual void DevToolsTargetReady() {}
protected:
Observer() {}
@@ -43,6 +42,14 @@ class HEADLESS_EXPORT HeadlessWebContents {
virtual void AddObserver(Observer* observer) = 0;
virtual void RemoveObserver(Observer* observer) = 0;
+ // Return a DevTools target corresponding to this tab. Note that this method
+ // won't return a valid value until Observer::DevToolsTargetReady has been
+ // signaled.
+ virtual HeadlessDevToolsTarget* GetDevToolsTarget() = 0;
+
+ // Close this page. |HeadlessWebContents| object will be destroyed.
+ virtual void Close() = 0;
+
private:
friend class HeadlessWebContentsImpl;
HeadlessWebContents() {}
diff --git a/chromium/headless/public/internal/message_dispatcher.h b/chromium/headless/public/internal/message_dispatcher.h
new file mode 100644
index 00000000000..ea939782b84
--- /dev/null
+++ b/chromium/headless/public/internal/message_dispatcher.h
@@ -0,0 +1,35 @@
+// 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_PUBLIC_INTERNAL_MESSAGE_DISPATCHER_H_
+#define HEADLESS_PUBLIC_INTERNAL_MESSAGE_DISPATCHER_H_
+
+namespace headless {
+namespace internal {
+
+// An internal interface for sending DevTools messages from the domain agents.
+class MessageDispatcher {
+ public:
+ virtual void SendMessage(
+ const char* method,
+ std::unique_ptr<base::Value> params,
+ base::Callback<void(const base::Value&)> callback) = 0;
+ virtual void SendMessage(const char* method,
+ std::unique_ptr<base::Value> params,
+ base::Callback<void()> callback) = 0;
+ virtual void SendMessage(
+ const char* method,
+ base::Callback<void(const base::Value&)> callback) = 0;
+ virtual void SendMessage(const char* method,
+ base::Callback<void()> callback) = 0;
+
+ virtual void RegisterEventHandler(
+ const char* method,
+ base::Callback<void(const base::Value&)> callback) = 0;
+};
+
+} // namespace internal
+} // namespace headless
+
+#endif // HEADLESS_PUBLIC_INTERNAL_MESSAGE_DISPATCHER_H_
diff --git a/chromium/headless/public/internal/value_conversions.h b/chromium/headless/public/internal/value_conversions.h
new file mode 100644
index 00000000000..17c6eda3f22
--- /dev/null
+++ b/chromium/headless/public/internal/value_conversions.h
@@ -0,0 +1,163 @@
+// 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_PUBLIC_INTERNAL_VALUE_CONVERSIONS_H_
+#define HEADLESS_PUBLIC_INTERNAL_VALUE_CONVERSIONS_H_
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "headless/public/util/error_reporter.h"
+
+namespace headless {
+namespace internal {
+
+// Generic conversion from a type to a base::Value. Implemented in
+// type_conversions.h after all type-specific ToValueImpls have been defined.
+template <typename T>
+std::unique_ptr<base::Value> ToValue(const T& value);
+
+// Generic conversion from a base::Value to a type. Note that this generic
+// variant is never defined. Instead, we declare a specific template
+// specialization for all the used types.
+template <typename T>
+struct FromValue {
+ static std::unique_ptr<T> Parse(const base::Value& value,
+ ErrorReporter* errors);
+};
+
+// ToValueImpl is a helper used by the ToValue template for dispatching into
+// type-specific serializers. It uses a dummy |T*| argument as a way to
+// partially specialize vector types.
+template <typename T>
+std::unique_ptr<base::Value> ToValueImpl(int value, T*) {
+ return base::WrapUnique(new base::FundamentalValue(value));
+}
+
+template <typename T>
+std::unique_ptr<base::Value> ToValueImpl(double value, T*) {
+ return base::WrapUnique(new base::FundamentalValue(value));
+}
+
+template <typename T>
+std::unique_ptr<base::Value> ToValueImpl(bool value, T*) {
+ return base::WrapUnique(new base::FundamentalValue(value));
+}
+
+template <typename T>
+std::unique_ptr<base::Value> ToValueImpl(const std::string& value, T*) {
+ return base::WrapUnique(new base::StringValue(value));
+}
+
+template <typename T>
+std::unique_ptr<base::Value> ToValueImpl(const base::Value& value, T*) {
+ return value.CreateDeepCopy();
+}
+
+template <typename T>
+std::unique_ptr<base::Value> ToValueImpl(const std::vector<T>& vector,
+ const std::vector<T>*) {
+ std::unique_ptr<base::ListValue> result(new base::ListValue());
+ for (const auto& it : vector)
+ result->Append(ToValue(it));
+ return std::move(result);
+}
+
+template <typename T>
+std::unique_ptr<base::Value> ToValueImpl(const std::unique_ptr<T>& value,
+ std::unique_ptr<T>*) {
+ return ToValue(value.get());
+}
+
+// FromValue specializations for basic types.
+template <>
+struct FromValue<bool> {
+ static bool Parse(const base::Value& value, ErrorReporter* errors) {
+ bool result = false;
+ if (!value.GetAsBoolean(&result))
+ errors->AddError("boolean value expected");
+ return result;
+ }
+};
+
+template <>
+struct FromValue<int> {
+ static int Parse(const base::Value& value, ErrorReporter* errors) {
+ int result = 0;
+ if (!value.GetAsInteger(&result))
+ errors->AddError("integer value expected");
+ return result;
+ }
+};
+
+template <>
+struct FromValue<double> {
+ static double Parse(const base::Value& value, ErrorReporter* errors) {
+ double result = 0;
+ if (!value.GetAsDouble(&result))
+ errors->AddError("double value expected");
+ return result;
+ }
+};
+
+template <>
+struct FromValue<std::string> {
+ static std::string Parse(const base::Value& value, ErrorReporter* errors) {
+ std::string result;
+ if (!value.GetAsString(&result))
+ errors->AddError("string value expected");
+ return result;
+ }
+};
+
+template <>
+struct FromValue<base::DictionaryValue> {
+ static std::unique_ptr<base::DictionaryValue> Parse(const base::Value& value,
+ ErrorReporter* errors) {
+ const base::DictionaryValue* result;
+ if (!value.GetAsDictionary(&result)) {
+ errors->AddError("dictionary value expected");
+ return nullptr;
+ }
+ return result->CreateDeepCopy();
+ }
+};
+
+template <>
+struct FromValue<base::Value> {
+ static std::unique_ptr<base::Value> Parse(const base::Value& value,
+ ErrorReporter* errors) {
+ return value.CreateDeepCopy();
+ }
+};
+
+template <typename T>
+struct FromValue<std::unique_ptr<T>> {
+ static std::unique_ptr<T> Parse(const base::Value& value,
+ ErrorReporter* errors) {
+ return FromValue<T>::Parse(value, errors);
+ }
+};
+
+template <typename T>
+struct FromValue<std::vector<T>> {
+ static std::vector<T> Parse(const base::Value& value, ErrorReporter* errors) {
+ std::vector<T> result;
+ const base::ListValue* list;
+ if (!value.GetAsList(&list)) {
+ errors->AddError("list value expected");
+ return result;
+ }
+ errors->Push();
+ for (const auto& item : *list)
+ result.push_back(FromValue<T>::Parse(*item, errors));
+ errors->Pop();
+ return result;
+ }
+};
+
+} // namespace internal
+} // namespace headless
+
+#endif // HEADLESS_PUBLIC_INTERNAL_VALUE_CONVERSIONS_H_
diff --git a/chromium/headless/public/util/error_reporter.cc b/chromium/headless/public/util/error_reporter.cc
new file mode 100644
index 00000000000..54c0758b71d
--- /dev/null
+++ b/chromium/headless/public/util/error_reporter.cc
@@ -0,0 +1,51 @@
+// 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/public/util/error_reporter.h"
+
+#include <sstream>
+
+#include "base/logging.h"
+
+namespace headless {
+
+ErrorReporter::ErrorReporter() {}
+
+ErrorReporter::~ErrorReporter() {}
+
+void ErrorReporter::Push() {
+ path_.push_back(nullptr);
+}
+
+void ErrorReporter::Pop() {
+ path_.pop_back();
+}
+
+void ErrorReporter::SetName(const char* name) {
+ DCHECK(!path_.empty());
+ path_[path_.size() - 1] = name;
+}
+
+void ErrorReporter::AddError(base::StringPiece description) {
+ std::stringstream error;
+ for (size_t i = 0; i < path_.size(); i++) {
+ if (!path_[i]) {
+ DCHECK_EQ(i + 1, path_.size());
+ break;
+ }
+ if (i)
+ error << '.';
+ error << path_[i];
+ }
+ if (error.tellp())
+ error << ": ";
+ error << description;
+ errors_.push_back(error.str());
+}
+
+bool ErrorReporter::HasErrors() const {
+ return !errors_.empty();
+}
+
+} // namespace headless
diff --git a/chromium/headless/public/util/error_reporter.h b/chromium/headless/public/util/error_reporter.h
new file mode 100644
index 00000000000..d7eb5e96d21
--- /dev/null
+++ b/chromium/headless/public/util/error_reporter.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_PUBLIC_UTIL_ERROR_REPORTER_H_
+#define HEADLESS_PUBLIC_UTIL_ERROR_REPORTER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/strings/string_piece.h"
+#include "headless/public/headless_export.h"
+
+namespace headless {
+
+// Tracks errors which are encountered while parsing client API types.
+class HEADLESS_EXPORT ErrorReporter {
+ public:
+ ErrorReporter();
+ ~ErrorReporter();
+
+ // Enter a new nested parsing context. It will initially have a null name.
+ void Push();
+
+ // Leave the current parsing context, returning to the previous one.
+ void Pop();
+
+ // Set the name of the current parsing context. |name| must be a string with
+ // application lifetime.
+ void SetName(const char* name);
+
+ // Report an error in the current parsing context.
+ void AddError(base::StringPiece description);
+
+ // Returns true if any errors have been reported so far.
+ bool HasErrors() const;
+
+ // Returns a list of reported errors.
+ const std::vector<std::string>& errors() const { return errors_; }
+
+ private:
+ std::vector<const char*> path_;
+ std::vector<std::string> errors_;
+};
+
+} // namespace headless
+
+#endif // HEADLESS_PUBLIC_UTIL_ERROR_REPORTER_H_
diff --git a/chromium/headless/public/util/error_reporter_unittest.cc b/chromium/headless/public/util/error_reporter_unittest.cc
new file mode 100644
index 00000000000..f23ced9ef29
--- /dev/null
+++ b/chromium/headless/public/util/error_reporter_unittest.cc
@@ -0,0 +1,52 @@
+// 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/public/util/error_reporter.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace headless {
+
+TEST(ErrorReporterTest, NoErrors) {
+ ErrorReporter reporter;
+ EXPECT_FALSE(reporter.HasErrors());
+ EXPECT_TRUE(reporter.errors().empty());
+}
+
+TEST(ErrorReporterTest, TopLevelErrors) {
+ ErrorReporter reporter;
+ reporter.AddError("instructions unclear");
+ reporter.AddError("head stuck in std::unordered_map");
+ EXPECT_TRUE(reporter.HasErrors());
+ EXPECT_EQ(2u, reporter.errors().size());
+ EXPECT_EQ("instructions unclear", reporter.errors()[0]);
+ EXPECT_EQ("head stuck in std::unordered_map", reporter.errors()[1]);
+}
+
+TEST(ErrorReporterTest, UnnamedContext) {
+ ErrorReporter reporter;
+ reporter.Push();
+ reporter.AddError("lp0 is on fire");
+ reporter.Pop();
+ EXPECT_TRUE(reporter.HasErrors());
+ EXPECT_EQ(1u, reporter.errors().size());
+ EXPECT_EQ("lp0 is on fire", reporter.errors()[0]);
+}
+
+TEST(ErrorReporterTest, NestedContexts) {
+ ErrorReporter reporter;
+ reporter.Push();
+ reporter.SetName("ship");
+ reporter.Push();
+ reporter.SetName("front");
+ reporter.AddError("fell off");
+ reporter.Pop();
+ reporter.AddError("uh oh");
+ reporter.Pop();
+ EXPECT_TRUE(reporter.HasErrors());
+ EXPECT_EQ(2u, reporter.errors().size());
+ EXPECT_EQ("ship.front: fell off", reporter.errors()[0]);
+ EXPECT_EQ("ship: uh oh", reporter.errors()[1]);
+}
+
+} // namespace headless
diff --git a/chromium/headless/public/util/maybe.h b/chromium/headless/public/util/maybe.h
deleted file mode 100644
index 18540a3634f..00000000000
--- a/chromium/headless/public/util/maybe.h
+++ /dev/null
@@ -1,92 +0,0 @@
-// 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_PUBLIC_UTIL_MAYBE_H_
-#define HEADLESS_PUBLIC_UTIL_MAYBE_H_
-
-#include <algorithm>
-
-#include "base/logging.h"
-#include "base/macros.h"
-
-namespace headless {
-
-// A simple Maybe which may or may not have a value. Based on v8::Maybe.
-template <typename T>
-class Maybe {
- public:
- Maybe() : has_value_(false) {}
-
- bool IsNothing() const { return !has_value_; }
- bool IsJust() const { return has_value_; }
-
- // Will crash if the Maybe<> is nothing.
- T& FromJust() {
- DCHECK(IsJust());
- return value_;
- }
- const T& FromJust() const {
- DCHECK(IsJust());
- return value_;
- }
-
- T FromMaybe(const T& default_value) const {
- return has_value_ ? value_ : default_value;
- }
-
- bool operator==(const Maybe& other) const {
- return (IsJust() == other.IsJust()) &&
- (!IsJust() || FromJust() == other.FromJust());
- }
-
- bool operator!=(const Maybe& other) const { return !operator==(other); }
-
- Maybe& operator=(Maybe&& other) {
- has_value_ = other.has_value_;
- value_ = std::move(other.value_);
- return *this;
- }
-
- Maybe& operator=(const Maybe& other) {
- has_value_ = other.has_value_;
- value_ = other.value_;
- return *this;
- }
-
- Maybe(const Maybe& other) = default;
- Maybe(Maybe&& other) = default;
-
- private:
- template <class U>
- friend Maybe<U> Nothing();
- template <class U>
- friend Maybe<U> Just(const U& u);
- template <class U>
- friend Maybe<typename std::remove_reference<U>::type> Just(U&& u);
-
- explicit Maybe(const T& t) : has_value_(true), value_(t) {}
- explicit Maybe(T&& t) : has_value_(true), value_(std::move(t)) {}
-
- bool has_value_;
- T value_;
-};
-
-template <class T>
-Maybe<T> Nothing() {
- return Maybe<T>();
-}
-
-template <class T>
-Maybe<T> Just(const T& t) {
- return Maybe<T>(t);
-}
-
-template <class T>
-Maybe<typename std::remove_reference<T>::type> Just(T&& t) {
- return Maybe<typename std::remove_reference<T>::type>(std::move(t));
-}
-
-} // namespace headless
-
-#endif // HEADLESS_PUBLIC_UTIL_MAYBE_H_
diff --git a/chromium/headless/public/util/maybe_unittest.cc b/chromium/headless/public/util/maybe_unittest.cc
deleted file mode 100644
index fb926961df5..00000000000
--- a/chromium/headless/public/util/maybe_unittest.cc
+++ /dev/null
@@ -1,83 +0,0 @@
-// 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/public/util/maybe.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace headless {
-namespace {
-
-class MoveOnlyType {
- public:
- MoveOnlyType() {}
- MoveOnlyType(MoveOnlyType&& other) {}
-
- void operator=(MoveOnlyType&& other) {}
-
- private:
- DISALLOW_COPY_AND_ASSIGN(MoveOnlyType);
-};
-
-} // namespace
-
-TEST(MaybeTest, Nothing) {
- Maybe<int> maybe;
- EXPECT_TRUE(maybe.IsNothing());
- EXPECT_FALSE(maybe.IsJust());
-}
-
-TEST(MaybeTest, Just) {
- Maybe<int> maybe = Just(1);
- EXPECT_FALSE(maybe.IsNothing());
- EXPECT_TRUE(maybe.IsJust());
- EXPECT_EQ(1, maybe.FromJust());
-
- const Maybe<int> const_maybe = Just(2);
- EXPECT_EQ(2, const_maybe.FromJust());
-}
-
-TEST(MaybeTest, Equality) {
- Maybe<int> a;
- Maybe<int> b;
- EXPECT_EQ(a, b);
- EXPECT_EQ(b, a);
-
- a = Just(1);
- EXPECT_NE(a, b);
- EXPECT_NE(b, a);
-
- b = Just(2);
- EXPECT_NE(a, b);
- EXPECT_NE(b, a);
-
- b = Just(1);
- EXPECT_EQ(a, b);
- EXPECT_EQ(b, a);
-}
-
-TEST(MaybeTest, Assignment) {
- Maybe<int> a = Just(1);
- Maybe<int> b = Nothing<int>();
- EXPECT_NE(a, b);
-
- b = a;
- EXPECT_EQ(a, b);
-}
-
-TEST(MaybeTest, MoveOnlyType) {
- MoveOnlyType value;
- Maybe<MoveOnlyType> a = Just(std::move(value));
- EXPECT_TRUE(a.IsJust());
-
- Maybe<MoveOnlyType> b = Just(MoveOnlyType());
- EXPECT_TRUE(b.IsJust());
-
- Maybe<MoveOnlyType> c = Nothing<MoveOnlyType>();
- c = std::move(a);
- EXPECT_TRUE(c.IsJust());
-
- MoveOnlyType d = std::move(b.FromJust());
-}
-
-} // namespace headless