diff options
Diffstat (limited to 'chromium/headless')
59 files changed, 2173 insertions, 764 deletions
diff --git a/chromium/headless/BUILD.gn b/chromium/headless/BUILD.gn index 8e5970bde4a..9c9d392f436 100644 --- a/chromium/headless/BUILD.gn +++ b/chromium/headless/BUILD.gn @@ -2,6 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//build/config/chrome_build.gni") import("//mojo/public/tools/bindings/mojom.gni") import("//testing/test.gni") import("//tools/grit/grit_rule.gni") @@ -22,6 +23,7 @@ repack("pak") { "$root_gen_dir/blink/devtools_resources.pak", "$root_gen_dir/blink/public/resources/blink_image_resources_100_percent.pak", "$root_gen_dir/blink/public/resources/blink_resources.pak", + "$root_gen_dir/components/strings/components_strings_en-US.pak", "$root_gen_dir/content/app/resources/content_resources_100_percent.pak", "$root_gen_dir/content/app/strings/content_strings_en-US.pak", "$root_gen_dir/content/browser/tracing/tracing_resources.pak", @@ -34,8 +36,15 @@ repack("pak") { "$root_gen_dir/ui/strings/ui_strings_en-US.pak", ] + if (is_chrome_branded) { + sources += [ "${root_gen_dir}/components/strings/components_google_chrome_strings_en-US.pak" ] + } else { + sources += [ "${root_gen_dir}/components/strings/components_chromium_strings_en-US.pak" ] + } + deps = [ ":headless_lib_resources_grit", + "//components/strings", "//content:resources", "//content/app/resources", "//content/app/strings", @@ -59,76 +68,59 @@ grit("headless_lib_resources_grit") { ] } +devtools_domains = [ + "accessibility", + "animation", + "application_cache", + "cache_storage", + "console", + "css", + "database", + "debugger", + "device_orientation", + "dom", + "dom_debugger", + "dom_storage", + "emulation", + "heap_profiler", + "indexeddb", + "input", + "inspector", + "io", + "layer_tree", + "log", + "memory", + "network", + "page", + "profiler", + "rendering", + "runtime", + "security", + "service_worker", + "target", + "tracing", +] + generated_devtools_api = [ - "$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/browser.cc", - "$target_gen_dir/public/domains/browser.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/log.cc", - "$target_gen_dir/public/domains/log.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", + "$target_gen_dir/public/domains/type_conversions.h", ] +foreach(domain, devtools_domains) { + generated_devtools_api += [ + "$target_gen_dir/public/domains/" + domain + ".h", + "$target_gen_dir/public/devtools/domains/" + domain + ".cc", + "$target_gen_dir/public/devtools/domains/" + domain + ".h", + "$target_gen_dir/public/devtools/domains/types_" + domain + ".h", + "$target_gen_dir/public/devtools/domains/types_" + domain + ".cc", + "$target_gen_dir/public/devtools/internal/type_conversions_" + domain + + ".h", + "$target_gen_dir/public/devtools/internal/" + + "types_forward_declarations_" + domain + ".h", + ] +} action("gen_devtools_client_api") { - script = "//headless/lib/browser/client_api_generator.py" + script = "//headless/lib/browser/devtools_api/client_api_generator.py" deps = [ "//third_party/WebKit/Source/core/inspector:protocol_version", ] @@ -139,18 +131,21 @@ action("gen_devtools_client_api") { outputs = generated_devtools_api 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", + "lib/browser/devtools_api/deprecated_type_conversions_h.template", + "lib/browser/devtools_api/deprecated_types_h.template", + "lib/browser/devtools_api/domain_cc.template", + "lib/browser/devtools_api/domain_h.template", + "lib/browser/devtools_api/domain_type_conversions_h.template", + "lib/browser/devtools_api/domain_types_cc.template", + "lib/browser/devtools_api/domain_types_forward_declaration_h.template", + "lib/browser/devtools_api/domain_types_h.template", ] args = [ "--protocol", rebase_path(inputs[0], root_build_dir), "--output_dir", - rebase_path(target_gen_dir) + "/public/domains", + rebase_path(target_gen_dir) + "/public", ] } @@ -178,8 +173,8 @@ static_library("headless_lib") { "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/browser/headless_window_parenting_client.cc", + "lib/browser/headless_window_parenting_client.h", "lib/headless_content_client.cc", "lib/headless_content_client.h", "lib/headless_content_main_delegate.cc", @@ -204,6 +199,8 @@ static_library("headless_lib") { "public/util/deterministic_dispatcher.h", "public/util/deterministic_http_protocol_handler.cc", "public/util/deterministic_http_protocol_handler.h", + "public/util/dom_tree_extractor.cc", + "public/util/dom_tree_extractor.h", "public/util/error_reporter.cc", "public/util/error_reporter.h", "public/util/expedited_dispatcher.cc", @@ -231,14 +228,17 @@ static_library("headless_lib") { ":gen_devtools_client_api", ":pak", "//base", + "//components/security_state/content", + "//components/security_state/core", "//content/public/app:both", "//content/public/browser", "//content/public/child", "//content/public/common", + "//content/public/common:service_names", "//content/public/renderer", "//content/public/utility", "//net", - "//services/shell/public/cpp", + "//services/service_manager/public/cpp", "//third_party/mesa:osmesa", "//ui/aura", "//ui/base", @@ -255,7 +255,6 @@ group("headless_tests") { testonly = true deps = [ - ":client_api_generator_tests", ":headless_browsertests", ":headless_unittests", ] @@ -281,23 +280,6 @@ test("headless_unittests") { ] } -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), - ] -} - mojom("embedder_mojo_for_testing") { sources = [ "lib/embedder_test.mojom", @@ -337,6 +319,7 @@ test("headless_browsertests") { "lib/headless_browser_context_browsertest.cc", "lib/headless_devtools_client_browsertest.cc", "lib/headless_web_contents_browsertest.cc", + "public/util/dom_tree_extractor_browsertest.cc", "test/headless_browser_test.cc", "test/headless_browser_test.h", "test/headless_test_launcher.cc", @@ -363,14 +346,25 @@ test("headless_browsertests") { ] } -executable("headless_shell") { +static_library("headless_shell_lib") { sources = [ "app/headless_shell.cc", "app/headless_shell_switches.cc", "app/headless_shell_switches.h", + "public/headless_shell.h", ] deps = [ "//headless:headless_lib", ] } + +executable("headless_shell") { + sources = [ + "app/headless_shell_main.cc", + ] + + deps = [ + "//headless:headless_shell_lib", + ] +} diff --git a/chromium/headless/DEPS b/chromium/headless/DEPS index cc93e6b7e6c..500e0224908 100644 --- a/chromium/headless/DEPS +++ b/chromium/headless/DEPS @@ -9,5 +9,5 @@ include_rules = [ "+ui/gfx/geometry", "+ui/gl", "+ui/ozone/public", - "+services/shell/public", + "+services/service_manager/public", ] diff --git a/chromium/headless/README.md b/chromium/headless/README.md index 9e3c7b02eba..06128d79b66 100644 --- a/chromium/headless/README.md +++ b/chromium/headless/README.md @@ -66,3 +66,14 @@ web pages. Its main classes are: 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. + +## Documentation + +* [Virtual Time in Blink](https://docs.google.com/document/d/1y9kdt_zezt7pbey6uzvt1dgklwc1ob_vy4nzo1zbqmo/edit#heading=h.tn3gd1y9ifml) +* [Headless Chrome architecture](https://docs.google.com/document/d/11zIkKkLBocofGgoTeeyibB2TZ_k7nR78v7kNelCatUE/edit) +* [Headless Chrome C++ DevTools API](https://docs.google.com/document/d/1rlqcp8nk-ZQvldNJWdbaMbwfDbJoOXvahPCDoPGOwhQ/edit#heading=h.ng2bxb15li9a) +* [Session isolation in Headless Chrome](https://docs.google.com/document/d/1XAKvrxtSEoe65vNghSWC5S3kJ--z2Zpt2UWW1Fi8GiM/edit) +* [Headless Chrome mojo service](https://docs.google.com/document/d/1Fr6_DJH6OK9rG3-ibMvRPTNnHsAXPk0VzxxiuJDSK3M/edit#heading=h.qh0udvlk963d) +* [Controlling BeginFrame through DevTools](https://docs.google.com/document/d/1LVMYDkfjrrX9PNkrD8pJH5-Np_XUTQHIuJ8IEOirQH4/edit?ts=57d96dbd#heading=h.ndv831lc9uf0) +* [Viewport bounds and scale for screenshots](https://docs.google.com/document/d/1VTcYz4q_x0f1O5IVrvRX4u1DVd_K34IVUl1VULLTCWw/edit#heading=h.ndv831lc9uf0) +* [BlinkOn 6 presentation slides](https://docs.google.com/presentation/d/1gqK9F4lGAY3TZudAtdcxzMQNEE7PcuQrGu83No3l0lw/edit#slide=id.p) diff --git a/chromium/headless/app/headless_shell.cc b/chromium/headless/app/headless_shell.cc index c2e3690f53d..b57fc5f9070 100644 --- a/chromium/headless/app/headless_shell.cc +++ b/chromium/headless/app/headless_shell.cc @@ -18,9 +18,9 @@ #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/emulation.h" -#include "headless/public/domains/page.h" -#include "headless/public/domains/runtime.h" +#include "headless/public/devtools/domains/emulation.h" +#include "headless/public/devtools/domains/page.h" +#include "headless/public/devtools/domains/runtime.h" #include "headless/public/headless_browser.h" #include "headless/public/headless_devtools_client.h" #include "headless/public/headless_devtools_target.h" @@ -33,14 +33,7 @@ #include "net/base/net_errors.h" #include "ui/gfx/geometry/size.h" -using headless::HeadlessBrowser; -using headless::HeadlessBrowserContext; -using headless::HeadlessDevToolsClient; -using headless::HeadlessWebContents; -namespace emulation = headless::emulation; -namespace page = headless::page; -namespace runtime = headless::runtime; - +namespace headless { namespace { // Address where to listen to incoming DevTools connections. const char kDevToolsHttpServerAddress[] = "127.0.0.1"; @@ -59,7 +52,7 @@ bool ParseWindowSize(std::string window_size, gfx::Size* parsed_window_size) { } } // namespace -// A sample application which demonstrates the use of the headless API. +// An application which implements a simple headless browser. class HeadlessShell : public HeadlessWebContents::Observer, emulation::ExperimentalObserver, page::Observer { @@ -78,24 +71,24 @@ class HeadlessShell : public HeadlessWebContents::Observer, HeadlessBrowserContext::Builder context_builder = browser_->CreateBrowserContextBuilder(); if (base::CommandLine::ForCurrentProcess()->HasSwitch( - headless::switches::kDeterministicFetch)) { + switches::kDeterministicFetch)) { deterministic_dispatcher_.reset( - new headless::DeterministicDispatcher(browser_->BrowserIOThread())); + new DeterministicDispatcher(browser_->BrowserIOThread())); - headless::ProtocolHandlerMap protocol_handlers; + ProtocolHandlerMap protocol_handlers; protocol_handlers[url::kHttpScheme] = - base::MakeUnique<headless::DeterministicHttpProtocolHandler>( + base::MakeUnique<DeterministicHttpProtocolHandler>( deterministic_dispatcher_.get(), browser->BrowserIOThread()); protocol_handlers[url::kHttpsScheme] = - base::MakeUnique<headless::DeterministicHttpProtocolHandler>( + base::MakeUnique<DeterministicHttpProtocolHandler>( deterministic_dispatcher_.get(), browser->BrowserIOThread()); context_builder.SetProtocolHandlers(std::move(protocol_handlers)); } if (base::CommandLine::ForCurrentProcess()->HasSwitch( - headless::switches::kHideScrollbars)) { + switches::kHideScrollbars)) { context_builder.SetOverrideWebPreferencesCallback( - base::Bind([](headless::WebPreferences* preferences) { + base::Bind([](WebPreferences* preferences) { preferences->hide_scrollbars = true; })); } @@ -147,10 +140,10 @@ class HeadlessShell : public HeadlessWebContents::Observer, devtools_client_->GetEmulation()->GetExperimental()->AddObserver(this); if (base::CommandLine::ForCurrentProcess()->HasSwitch( - headless::switches::kVirtualTimeBudget)) { + switches::kVirtualTimeBudget)) { std::string budget_ms_ascii = base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( - headless::switches::kVirtualTimeBudget); + switches::kVirtualTimeBudget); int budget_ms; CHECK(base::StringToInt(budget_ms_ascii, &budget_ms)) << "Expected an integer value for --virtual-time-budget="; @@ -200,7 +193,7 @@ class HeadlessShell : public HeadlessWebContents::Observer, // page::Observer implementation: void OnLoadEventFired(const page::LoadEventFiredParams& params) override { if (base::CommandLine::ForCurrentProcess()->HasSwitch( - headless::switches::kVirtualTimeBudget)) { + switches::kVirtualTimeBudget)) { return; } OnPageReady(); @@ -211,17 +204,16 @@ class HeadlessShell : public HeadlessWebContents::Observer, return; processed_page_ready_ = true; - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - headless::switches::kDumpDom)) { + if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpDom)) { FetchDom(); } else if (base::CommandLine::ForCurrentProcess()->HasSwitch( - headless::switches::kRepl)) { + switches::kRepl)) { std::cout << "Type a Javascript expression to evaluate or \"quit\" to exit." << std::endl; InputExpression(); } else if (base::CommandLine::ForCurrentProcess()->HasSwitch( - headless::switches::kScreenshot)) { + switches::kScreenshot)) { CaptureScreenshot(); } else { Shutdown(); @@ -281,7 +273,7 @@ class HeadlessShell : public HeadlessWebContents::Observer, std::unique_ptr<page::CaptureScreenshotResult> result) { base::FilePath file_name = base::CommandLine::ForCurrentProcess()->GetSwitchValuePath( - headless::switches::kScreenshot); + switches::kScreenshot); if (file_name.empty()) { file_name = base::FilePath().AppendASCII(kDefaultScreenshotFileName); } @@ -350,7 +342,7 @@ class HeadlessShell : public HeadlessWebContents::Observer, bool RemoteDebuggingEnabled() const { const base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess(); - return command_line.HasSwitch(switches::kRemoteDebuggingPort); + return command_line.HasSwitch(::switches::kRemoteDebuggingPort); } private: @@ -361,23 +353,23 @@ class HeadlessShell : public HeadlessWebContents::Observer, bool processed_page_ready_; std::unique_ptr<net::FileStream> screenshot_file_stream_; HeadlessBrowserContext* browser_context_; - std::unique_ptr<headless::DeterministicDispatcher> deterministic_dispatcher_; + std::unique_ptr<DeterministicDispatcher> deterministic_dispatcher_; DISALLOW_COPY_AND_ASSIGN(HeadlessShell); }; -int main(int argc, const char** argv) { - headless::RunChildProcessIfNeeded(argc, argv); +int HeadlessShellMain(int argc, const char** argv) { + RunChildProcessIfNeeded(argc, argv); HeadlessShell shell; HeadlessBrowser::Options::Builder builder(argc, argv); // Enable devtools if requested. base::CommandLine command_line(argc, argv); - if (command_line.HasSwitch(switches::kRemoteDebuggingPort)) { + if (command_line.HasSwitch(::switches::kRemoteDebuggingPort)) { std::string address = kDevToolsHttpServerAddress; - if (command_line.HasSwitch(headless::switches::kRemoteDebuggingAddress)) { - address = command_line.GetSwitchValueASCII( - headless::switches::kRemoteDebuggingAddress); + if (command_line.HasSwitch(switches::kRemoteDebuggingAddress)) { + address = + command_line.GetSwitchValueASCII(switches::kRemoteDebuggingAddress); net::IPAddress parsed_address; if (!net::ParseURLHostnameToAddress(address, &parsed_address)) { LOG(ERROR) << "Invalid devtools server address"; @@ -386,7 +378,7 @@ int main(int argc, const char** argv) { } int parsed_port; std::string port_str = - command_line.GetSwitchValueASCII(switches::kRemoteDebuggingPort); + command_line.GetSwitchValueASCII(::switches::kRemoteDebuggingPort); if (!base::StringToInt(port_str, &parsed_port) || !base::IsValueInRangeForNumericType<uint16_t>(parsed_port)) { LOG(ERROR) << "Invalid devtools server port"; @@ -399,9 +391,9 @@ int main(int argc, const char** argv) { devtools_address, base::checked_cast<uint16_t>(parsed_port))); } - if (command_line.HasSwitch(headless::switches::kProxyServer)) { + if (command_line.HasSwitch(switches::kProxyServer)) { std::string proxy_server = - command_line.GetSwitchValueASCII(headless::switches::kProxyServer); + command_line.GetSwitchValueASCII(switches::kProxyServer); net::HostPortPair parsed_proxy_server = net::HostPortPair::FromString(proxy_server); if (parsed_proxy_server.host().empty() || !parsed_proxy_server.port()) { @@ -411,25 +403,25 @@ int main(int argc, const char** argv) { builder.SetProxyServer(parsed_proxy_server); } - if (command_line.HasSwitch(switches::kHostResolverRules)) { + if (command_line.HasSwitch(::switches::kHostResolverRules)) { builder.SetHostResolverRules( - command_line.GetSwitchValueASCII(switches::kHostResolverRules)); + command_line.GetSwitchValueASCII(::switches::kHostResolverRules)); } - if (command_line.HasSwitch(headless::switches::kUseGL)) { + if (command_line.HasSwitch(switches::kUseGL)) { builder.SetGLImplementation( - command_line.GetSwitchValueASCII(headless::switches::kUseGL)); + command_line.GetSwitchValueASCII(switches::kUseGL)); } - if (command_line.HasSwitch(headless::switches::kUserDataDir)) { + if (command_line.HasSwitch(switches::kUserDataDir)) { builder.SetUserDataDir( - command_line.GetSwitchValuePath(headless::switches::kUserDataDir)); + command_line.GetSwitchValuePath(switches::kUserDataDir)); builder.SetIncognitoMode(false); } - if (command_line.HasSwitch(headless::switches::kWindowSize)) { + if (command_line.HasSwitch(switches::kWindowSize)) { std::string window_size = - command_line.GetSwitchValueASCII(headless::switches::kWindowSize); + command_line.GetSwitchValueASCII(switches::kWindowSize); gfx::Size parsed_window_size; if (!ParseWindowSize(window_size, &parsed_window_size)) { LOG(ERROR) << "Malformed window size"; @@ -442,3 +434,5 @@ int main(int argc, const char** argv) { builder.Build(), base::Bind(&HeadlessShell::OnStart, base::Unretained(&shell))); } + +} // namespace headless diff --git a/chromium/headless/app/headless_shell_main.cc b/chromium/headless/app/headless_shell_main.cc new file mode 100644 index 00000000000..3ae4553b068 --- /dev/null +++ b/chromium/headless/app/headless_shell_main.cc @@ -0,0 +1,9 @@ +// 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/headless_shell.h" + +int main(int argc, const char** argv) { + return headless::HeadlessShellMain(argc, argv); +} diff --git a/chromium/headless/lib/browser/DEPS b/chromium/headless/lib/browser/DEPS index 75ab3c75a98..1704e22e758 100644 --- a/chromium/headless/lib/browser/DEPS +++ b/chromium/headless/lib/browser/DEPS @@ -1,3 +1,4 @@ include_rules = [ - "+ui/aura" + "+components/security_state", + "+ui/aura", ] diff --git a/chromium/headless/lib/browser/client_api_generator.py b/chromium/headless/lib/browser/devtools_api/client_api_generator.py index bb93ede97aa..ce96de211b4 100644 --- a/chromium/headless/lib/browser/client_api_generator.py +++ b/chromium/headless/lib/browser/devtools_api/client_api_generator.py @@ -3,9 +3,10 @@ # found in the LICENSE file. import argparse +import collections import os.path -import sys import re +import sys try: import json except ImportError: @@ -20,8 +21,9 @@ except ImportError: # 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')) +third_party_dir = os.path.normpath( + os.path.join(module_path, os.pardir, os.pardir, os.pardir, os.pardir, + 'third_party')) templates_dir = module_path # jinja2 is in chromium's third_party directory. @@ -61,14 +63,14 @@ def CamelCaseToHackerStyle(name): def SanitizeLiteral(literal): return { - # Rename null enumeration values to avoid a clash with the NULL macro. - 'null': 'none', - # Rename mathematical constants to avoid colliding with C macros. - 'Infinity': 'InfinityValue', - '-Infinity': 'NegativeInfinityValue', - 'NaN': 'NaNValue', - # Turn negative zero into a safe identifier. - '-0': 'NegativeZeroValue', + # Rename null enumeration values to avoid a clash with the NULL macro. + 'null': 'none', + # Rename mathematical constants to avoid colliding with C macros. + 'Infinity': 'InfinityValue', + '-Infinity': 'NegativeInfinityValue', + 'NaN': 'NaNValue', + # Turn negative zero into a safe identifier. + '-0': 'NegativeZeroValue', }.get(literal, literal) @@ -82,10 +84,10 @@ def InitializeJinjaEnv(cache_dir): 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, - 'sanitize_literal': SanitizeLiteral, + 'to_title_case': ToTitleCase, + 'dash_to_camelcase': DashToCamelCase, + 'camelcase_to_hacker_style': CamelCaseToHackerStyle, + 'sanitize_literal': SanitizeLiteral, }) jinja_env.add_extension('jinja2.ext.loopcontrols') return jinja_env @@ -113,108 +115,109 @@ def PatchFullQualifiedRefs(json_api): 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']), + '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']), + '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*', + '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, + '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*', + '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', + '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', + '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], + '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], } @@ -229,15 +232,15 @@ 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'], + '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'], } @@ -301,7 +304,8 @@ def SynthesizeEnumType(domain, owner, type): def SynthesizeCommandTypes(json_api): """Generate types for command parameters, return values and enum - properties.""" + properties. + """ for domain in json_api['domains']: if not 'types' in domain: domain['types'] = [] @@ -317,11 +321,11 @@ def SynthesizeCommandTypes(json_api): 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'] + '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: @@ -329,11 +333,11 @@ def SynthesizeCommandTypes(json_api): 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'] + 'id': ToTitleCase(command['name']) + 'Result', + 'type': 'object', + 'description': 'Result for the %s command.' % ToTitleCase( + command['name']), + 'properties': command['returns'] } domain['types'].append(result_type) @@ -352,18 +356,57 @@ def SynthesizeEventTypes(json_api): 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', []) + '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 InitializeDomainDependencies(json_api): + """For each domain create list of domains given domain depends on, + including itself.""" + + direct_deps = collections.defaultdict(set) + + def GetDomainDepsFromRefs(domain_name, json): + if isinstance(json, list): + for value in json: + GetDomainDepsFromRefs(domain_name, value) + return + + if not isinstance(json, dict): + return + for value in json.itervalues(): + GetDomainDepsFromRefs(domain_name, value) + + if '$ref' in json: + if '.' in json['$ref']: + dep = json['$ref'].split('.')[0] + direct_deps[domain_name].add(dep) + + for domain in json_api['domains']: + direct_deps[domain['domain']] = set(domain.get('dependencies', [])) + GetDomainDepsFromRefs(domain['domain'], domain) + + def TraverseDependencies(domain, deps): + if domain in deps: + return + deps.add(domain) + + for dep in direct_deps[domain]: + TraverseDependencies(dep, deps) + + for domain in json_api['domains']: + domain_deps = set() + TraverseDependencies(domain['domain'], domain_deps) + domain['dependencies'] = sorted(domain_deps) + + def PatchExperimentalCommandsAndEvents(json_api): - """ - Mark all commands and events in experimental domains as experimental + """Mark all commands and events in experimental domains as experimental and make sure experimental commands have at least empty parameters and return values. """ @@ -375,10 +418,14 @@ def PatchExperimentalCommandsAndEvents(json_api): event['experimental'] = True +def EnsureDirectoryExists(path): + if not os.path.exists(path): + os.makedirs(path) + + def EnsureCommandsHaveParametersAndReturnTypes(json_api): - """ - Make sure all commands have at least empty parameters and return values. This - guarantees API compatibility if a previously experimental command is made + """Make sure all commands have at least empty parameters and return values. + This guarantees API compatibility if a previously experimental command is made stable. """ for domain in json_api['domains']: @@ -392,46 +439,98 @@ def EnsureCommandsHaveParametersAndReturnTypes(json_api): event['parameters'] = [] -def Generate(jinja_env, output_dirname, json_api, class_name, file_types): +def Generate(jinja_env, output_dirname, json_api, + class_name, file_types, file_name=None): + if file_name is None: + file_name = class_name + EnsureDirectoryExists(output_dirname) template_context = { - 'api': json_api, - 'join_arrays': JoinArrays, - 'resolve_type': ResolveType, - 'type_definition': TypeDefinition, + '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) + output_file = '%s/%s.%s' % (output_dirname, file_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): +def GeneratePerDomain(jinja_env, output_dirname, json_api, class_name, + file_types, domain_name_to_file_name_func): + EnsureDirectoryExists(output_dirname) 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': domain, + 'resolve_type': ResolveType, } domain_name = CamelCaseToHackerStyle(domain['domain']) - output_file = '%s/%s.%s' % (output_dirname, domain_name, file_type) + output_file = '%s/%s.%s' % (output_dirname, + domain_name_to_file_name_func(domain_name), + file_type) with open(output_file, 'w') as f: f.write(template.render(template_context)) +def GenerateDomains(jinja_env, output_dirname, json_api): + GeneratePerDomain( + jinja_env, os.path.join(output_dirname, 'devtools', 'domains'), json_api, + 'domain', ['cc', 'h'], + lambda domain_name: domain_name) + + # TODO(altimin): Remove this in 2017. + # Generate DOMAIN.h in the old directory for backwards compatibility. + GeneratePerDomain( + jinja_env, os.path.join(output_dirname, 'domains'), json_api, + 'deprecated_domain', ['h'], lambda domain_name: domain_name) + + +def GenerateTypes(jinja_env, output_dirname, json_api): + # Generate forward declarations for types. + GeneratePerDomain( + jinja_env, os.path.join(output_dirname, 'devtools', 'internal'), + json_api, 'domain_types_forward_declaration', ['h'], + lambda domain_name: 'types_forward_declaration_%s' % (domain_name, )) + # Generate types on per-domain basis. + GeneratePerDomain( + jinja_env, os.path.join(output_dirname, 'devtools', 'domains'), + json_api, 'domain_types', ['h', 'cc'], + lambda domain_name: 'types_%s' % (domain_name, )) + + # TODO(altimin): Remove this in 2017. + # Generate types.h for backwards compatibility. + Generate(jinja_env, os.path.join(output_dirname, 'domains'), json_api, + 'deprecated_types', ['h'], 'types') + + +def GenerateTypeConversions(jinja_env, output_dirname, json_api): + # Generate type conversions on per-domain basis. + GeneratePerDomain( + jinja_env, os.path.join(output_dirname, 'devtools', 'internal'), + json_api, 'domain_type_conversions', ['h'], + lambda domain_name: 'type_conversions_%s' % (domain_name, )) + + # TODO(altimin): Remove this in 2017. + # Generate type_conversions.h for backwards compatibility. + Generate(jinja_env, os.path.join(output_dirname, 'domains'), json_api, + 'deprecated_type_conversions', ['h'], 'type_conversions') + + if __name__ == '__main__': json_api, output_dirname = ParseArguments(sys.argv[1:]) jinja_env = InitializeJinjaEnv(output_dirname) + InitializeDomainDependencies(json_api) PatchExperimentalCommandsAndEvents(json_api) EnsureCommandsHaveParametersAndReturnTypes(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']) + GenerateDomains(jinja_env, output_dirname, json_api) + GenerateTypes(jinja_env, output_dirname, json_api) + GenerateTypeConversions(jinja_env, output_dirname, json_api) diff --git a/chromium/headless/lib/browser/client_api_generator_unittest.py b/chromium/headless/lib/browser/devtools_api/client_api_generator_unittest.py index d9d3f81dea7..f41ac0920a0 100644..100755 --- a/chromium/headless/lib/browser/client_api_generator_unittest.py +++ b/chromium/headless/lib/browser/devtools_api/client_api_generator_unittest.py @@ -1,8 +1,9 @@ +#!/usr/bin/env python + # 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 @@ -351,6 +352,65 @@ class ClientApiGeneratorTest(unittest.TestCase): types = json_api['domains'][0]['types'] self.assertListEqual(types, expected_types) + def test_InitializeDomainDependencies(self): + json_api = { + 'domains': [ + { + 'domain': 'Domain1', + 'types': [ + { + 'id': 'TestType', + 'type': 'object', + 'properties': [ + {'name': 'p1', 'type': 'object', '$ref': 'Domain2.TestType'}, + ], + }, + ], + }, + { + 'domain': 'Domain2', + 'dependencies': ['Domain3'], + 'types': [ + { + 'id': 'TestType', + 'type': 'object', + 'properties': [ + {'name': 'p1', 'type': 'object', '$ref': 'Domain1.TestType'}, + ], + }, + ], + }, + { + 'domain': 'Domain3', + }, + { + 'domain': 'Domain4', + 'dependencies': ['Domain1'], + }, + ] + } + client_api_generator.InitializeDomainDependencies(json_api) + + dependencies = [ { + 'domain': domain['domain'], + 'dependencies': domain['dependencies'] + } for domain in json_api['domains'] ] + + self.assertListEqual(dependencies, [ { + "domain": "Domain1", + "dependencies": ["Domain1", "Domain2", "Domain3"], + }, { + "domain": "Domain2", + "dependencies": ["Domain1", "Domain2", "Domain3"], + }, { + "domain": "Domain3", + "dependencies": ["Domain3"], + }, { + "domain": "Domain4", + "dependencies": ["Domain1", "Domain2", "Domain3", "Domain4"], + } + ]) + def test_PatchExperimentalDomains(self): json_api = { 'domains': [ @@ -453,10 +513,9 @@ class ClientApiGeneratorTest(unittest.TestCase): 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']) + client_api_generator.CreateTypeDefinitions(json_api) + client_api_generator.Generate(jinja_env, dirname, json_api, + 'deprecated_types', ['h']) # This is just a smoke test; we don't actually verify the generated output # here. finally: @@ -488,8 +547,9 @@ class ClientApiGeneratorTest(unittest.TestCase): try: dirname = tempfile.mkdtemp() jinja_env = client_api_generator.InitializeJinjaEnv(dirname) - client_api_generator.GenerateDomains(jinja_env, dirname, json_api, - 'domain', ['cc', 'h']) + client_api_generator.GeneratePerDomain( + jinja_env, dirname, json_api, + 'domain', ['cc', 'h'], lambda domain_name: domain_name) # This is just a smoke test; we don't actually verify the generated output # here. finally: @@ -497,10 +557,4 @@ class ClientApiGeneratorTest(unittest.TestCase): 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 + unittest.main(verbosity=2, exit=False, argv=sys.argv) diff --git a/chromium/headless/lib/browser/devtools_api/deprecated_domain_h.template b/chromium/headless/lib/browser/devtools_api/deprecated_domain_h.template new file mode 100644 index 00000000000..e430a104e8d --- /dev/null +++ b/chromium/headless/lib/browser/devtools_api/deprecated_domain_h.template @@ -0,0 +1,7 @@ +// 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/devtools/domains/{{domain.domain | camelcase_to_hacker_style }}.h" diff --git a/chromium/headless/lib/browser/devtools_api/deprecated_type_conversions_h.template b/chromium/headless/lib/browser/devtools_api/deprecated_type_conversions_h.template new file mode 100644 index 00000000000..4e17400c6ce --- /dev/null +++ b/chromium/headless/lib/browser/devtools_api/deprecated_type_conversions_h.template @@ -0,0 +1,14 @@ +// 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_ + +{% for domain in api.domains %} +#include "headless/public/devtools/internal/type_conversions_{{domain.domain | camelcase_to_hacker_style}}.h" +{% endfor %} + +#endif // HEADLESS_PUBLIC_DOMAINS_TYPE_CONVERSIONS_H_ diff --git a/chromium/headless/lib/browser/devtools_api/deprecated_types_h.template b/chromium/headless/lib/browser/devtools_api/deprecated_types_h.template new file mode 100644 index 00000000000..85b5379c5bf --- /dev/null +++ b/chromium/headless/lib/browser/devtools_api/deprecated_types_h.template @@ -0,0 +1,14 @@ +// 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_TYPES_H_ +#define HEADLESS_PUBLIC_DOMAINS_TYPES_H_ + +{% for domain in api.domains %} +#include "headless/public/devtools/domains/types_{{domain.domain | camelcase_to_hacker_style}}.h" +{% endfor %} + +#endif // HEADLESS_PUBLIC_DOMAINS_TYPES_H_ diff --git a/chromium/headless/lib/browser/domain_cc.template b/chromium/headless/lib/browser/devtools_api/domain_cc.template index 643afe749a8..88f28b1f56a 100644 --- a/chromium/headless/lib/browser/domain_cc.template +++ b/chromium/headless/lib/browser/devtools_api/domain_cc.template @@ -4,7 +4,7 @@ // 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 "headless/public/devtools/domains/{{domain.domain | camelcase_to_hacker_style}}.h" #include "base/bind.h" @@ -18,12 +18,26 @@ ExperimentalDomain* Domain::GetExperimental() { {% if "events" in domain %} void Domain::AddObserver(Observer* observer) { + RegisterEventHandlersIfNeeded(); observers_.AddObserver(observer); } void Domain::RemoveObserver(Observer* observer) { observers_.RemoveObserver(observer); } + +void Domain::RegisterEventHandlersIfNeeded() { + if (event_handlers_registered_) + return; + event_handlers_registered_ = true; + {# 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 %} {% for command in domain.commands %} @@ -102,22 +116,15 @@ void Domain::Dispatch{{event.name | to_title_case}}Event(const base::Value& para 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)); + for (ExperimentalObserver& observer : observers_) { + observer.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() {} @@ -129,6 +136,7 @@ ExperimentalDomain::~ExperimentalDomain() {} {% if "events" in domain %} void ExperimentalDomain::AddObserver(ExperimentalObserver* observer) { + RegisterEventHandlersIfNeeded(); observers_.AddObserver(observer); } diff --git a/chromium/headless/lib/browser/domain_h.template b/chromium/headless/lib/browser/devtools_api/domain_h.template index 2d146a70b52..73110412f43 100644 --- a/chromium/headless/lib/browser/domain_h.template +++ b/chromium/headless/lib/browser/devtools_api/domain_h.template @@ -4,13 +4,15 @@ // 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_ +#ifndef HEADLESS_PUBLIC_DEVTOOLS_DOMAINS_{{domain.domain | camelcase_to_hacker_style | upper}}_H_ +#define HEADLESS_PUBLIC_DEVTOOLS_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" +{% for domain_name in domain.dependencies %} +#include "headless/public/devtools/domains/types_{{domain_name | camelcase_to_hacker_style}}.h" +{% endfor %} #include "headless/public/headless_export.h" #include "headless/public/internal/message_dispatcher.h" @@ -120,7 +122,16 @@ class HEADLESS_EXPORT Domain { base::ObserverList<ExperimentalObserver> observers_; {% endif %} + {% if "events" in domain %} + protected: + void RegisterEventHandlersIfNeeded(); + + {% endif %} private: + {% if "events" in domain %} + bool event_handlers_registered_ = false; + + {% endif %} DISALLOW_COPY_AND_ASSIGN(Domain); }; @@ -152,4 +163,4 @@ class ExperimentalDomain : public Domain { } // namespace {{domain.domain | camelcase_to_hacker_style}} } // namespace headless -#endif // HEADLESS_PUBLIC_DOMAINS_{{domain.domain | camelcase_to_hacker_style | upper}}_H_ +#endif // HEADLESS_PUBLIC_DEVTOOLS_DOMAINS_{{domain.domain | camelcase_to_hacker_style | upper}}_H_ diff --git a/chromium/headless/lib/browser/type_conversions_h.template b/chromium/headless/lib/browser/devtools_api/domain_type_conversions_h.template index 1c918ac9ea3..f14b423a66a 100644 --- a/chromium/headless/lib/browser/type_conversions_h.template +++ b/chromium/headless/lib/browser/devtools_api/domain_type_conversions_h.template @@ -4,33 +4,32 @@ // 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_ +#ifndef HEADLESS_PUBLIC_DEVTOOLS_INTERNAL_TYPE_CONVERSIONS_{{domain.domain | camelcase_to_hacker_style | upper}}_H_ +#define HEADLESS_PUBLIC_DEVTOOLS_INTERNAL_TYPE_CONVERSIONS_{{domain.domain | camelcase_to_hacker_style | upper}}_H_ -#include "headless/public/domains/types.h" +#include "headless/public/devtools/domains/types_{{domain.domain | camelcase_to_hacker_style}}.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 %} +{% 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] | sanitize_literal | dash_to_camelcase | camelcase_to_hacker_style | upper %} + {% set default = namespace + '::' + type.id + '::' + type.enum[0] | sanitize_literal | dash_to_camelcase | camelcase_to_hacker_style | upper %} std::string string_value; if (!value.GetAsString(&string_value)) { errors->AddError("string enum value expected"); {# Return an arbitrary enum member -- the caller will just ignore it. #} return {{default}}; } - {% for literal in type.enum %} + {% for literal in type.enum %} if (string_value == "{{literal}}") return {{namespace}}::{{type.id}}::{{literal | sanitize_literal | dash_to_camelcase | camelcase_to_hacker_style | upper }}; - {% endfor %} + {% endfor %} errors->AddError("invalid enum value"); return {{default}}; } @@ -39,18 +38,18 @@ struct FromValue<{{namespace}}::{{type.id}}> { template <typename T> std::unique_ptr<base::Value> ToValueImpl(const {{namespace}}::{{type.id}}& value, T*) { switch (value) { - {% for literal in type.enum %} + {% for literal in type.enum %} case {{namespace}}::{{type.id}}::{{literal | sanitize_literal | dash_to_camelcase | camelcase_to_hacker_style | upper }}: return base::WrapUnique(new base::StringValue("{{literal}}")); - {% endfor %} + {% endfor %} }; NOTREACHED(); return nullptr; } - {% continue %} - {% endif %} + {% continue %} + {% endif %} - {% if not (type.type == "object") or not ("properties" in type) %}{% 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) { @@ -63,15 +62,9 @@ std::unique_ptr<base::Value> ToValueImpl(const {{namespace}}::{{type.id}}& value 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_ +#endif // HEADLESS_PUBLIC_DEVTOOLS_INTERNAL_TYPE_CONVERSIONS_{{domain.domain | camelcase_to_hacker_style | upper}}_H_ diff --git a/chromium/headless/lib/browser/types_cc.template b/chromium/headless/lib/browser/devtools_api/domain_types_cc.template index 5be72cf7924..bd1c2773dd4 100644 --- a/chromium/headless/lib/browser/types_cc.template +++ b/chromium/headless/lib/browser/devtools_api/domain_types_cc.template @@ -4,65 +4,29 @@ // 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" +{% for domain_name in domain.dependencies %} +#include "headless/public/devtools/domains/types_{{domain_name | camelcase_to_hacker_style}}.h" +{% endfor %} #include "base/memory/ptr_util.h" -#include "headless/public/domains/type_conversions.h" +{% for dep in domain.dependencies %} +#include "headless/public/devtools/internal/type_conversions_{{dep | camelcase_to_hacker_style}}.h" +{% endfor %} 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(); +std::unique_ptr<base::Value> ToValue(const T& value) { + return ToValueImpl(value, static_cast<T*>(nullptr)); } - {% 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 %} +{% 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(); @@ -77,24 +41,24 @@ std::unique_ptr<{{type.id}}> {{type.id}}::Parse(const base::Value& value, ErrorR 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" %} + {% 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 %} + {% if property.optional %} result->{{property.name | camelcase_to_hacker_style}}_ = internal::FromValue<{{resolve_type(property).raw_type}}>::Parse(*{{value_name}}, errors); - {% else %} + {% else %} result->{{property.name | camelcase_to_hacker_style}}_ = internal::FromValue<{{resolve_type(property).raw_type}}>::Parse(*{{value_name}}, errors); - {% endif %} - {% if property.optional %} + {% endif %} + {% if property.optional %} } - {% else %} + {% else %} } else { errors->AddError("required property missing: {{property.name}}"); } - {% endif %} - {% endfor %} + {% endif %} + {% endfor %} errors->Pop(); errors->Pop(); if (errors->HasErrors()) @@ -104,15 +68,15 @@ std::unique_ptr<{{type.id}}> {{type.id}}::Parse(const base::Value& value, ErrorR 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 %} + {% 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 %} + {% else %} result->Set("{{property.name}}", internal::ToValue({{type.to_raw_type % ("%s_" % property.name | camelcase_to_hacker_style)}})); - {% endif %} - {% endfor %} + {% endif %} + {% endfor %} return std::move(result); } @@ -123,8 +87,7 @@ std::unique_ptr<{{type.id}}> {{type.id}}::Clone() const { return result; } - {% endfor %} -} // namespace {{domain.domain | camelcase_to_hacker_style}} {% endfor %} -} // namespace headless +} // namespace {{domain.domain | camelcase_to_hacker_style}} +} // namespace headless diff --git a/chromium/headless/lib/browser/devtools_api/domain_types_forward_declaration_h.template b/chromium/headless/lib/browser/devtools_api/domain_types_forward_declaration_h.template new file mode 100644 index 00000000000..4bb223b04ab --- /dev/null +++ b/chromium/headless/lib/browser/devtools_api/domain_types_forward_declaration_h.template @@ -0,0 +1,44 @@ +// 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_DEVTOOLS_INTERNAL_TYPES_FORWARD_DECLARATION_{{domain.domain | camelcase_to_hacker_style | upper}}_H_ +#define HEADLESS_PUBLIC_DEVTOOLS_INTERNAL_TYPES_FORWARD_DECLARATION_{{domain.domain | camelcase_to_hacker_style | upper}}_H_ + +#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 { + +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 %} + +{% for type in domain.types %} + {% if "enum" in type %} +enum class {{type.id}} { + {% for literal in type.enum %} + {{ literal | sanitize_literal | dash_to_camelcase | camelcase_to_hacker_style | upper }}{{',' if not loop.last}} + {% endfor %} +}; + + {% endif %} +{% endfor %} +} // namespace {{domain.domain | camelcase_to_hacker_style}} + +} // namespace headless + +#endif // HEADLESS_PUBLIC_DEVTOOLS_INTERNAL_TYPES_FORWARD_DECLARATION_{{domain.domain | camelcase_to_hacker_style | upper}}_H_ diff --git a/chromium/headless/lib/browser/types_h.template b/chromium/headless/lib/browser/devtools_api/domain_types_h.template index 84b09cbeb24..f53f8ae1a52 100644 --- a/chromium/headless/lib/browser/types_h.template +++ b/chromium/headless/lib/browser/devtools_api/domain_types_h.template @@ -4,11 +4,14 @@ // 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_ +#ifndef HEADLESS_PUBLIC_DEVTOOLS_DOMAINS_TYPES_{{domain.domain | camelcase_to_hacker_style | upper}}_H_ +#define HEADLESS_PUBLIC_DEVTOOLS_DOMAINS_TYPES_{{domain.domain | camelcase_to_hacker_style | upper}}_H_ #include "base/optional.h" #include "base/values.h" +{% for domain_name in domain.dependencies %} +#include "headless/public/devtools/internal/types_forward_declaration_{{domain_name | camelcase_to_hacker_style}}.h" +{% endfor %} #include "headless/public/headless_export.h" #include "headless/public/util/error_reporter.h" @@ -16,67 +19,31 @@ 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 | sanitize_literal | dash_to_camelcase | camelcase_to_hacker_style | upper }}{{',' if not loop.last}} - {% endfor %} -}; - - {% endif %} - {% endfor %} -} // namespace {{domain.domain | camelcase_to_hacker_style}} - -{% endfor %} - -// ------------- Type and builder declarations. -{% for domain in api.domains %} +{% for type in domain.types %} + {% if not (type.type == "object") or not ("properties" in type) %}{% continue %}{% endif %} -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 %} + {% if type.description %} // {{type.description}} - {% endif %} + {% 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 %} + {% for property in type.properties %} - {% if property.description %} + {% if property.description %} // {{property.description}} - {% endif %} - {% if property.optional %} + {% 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 { DCHECK(Has{{property.name | to_title_case}}()); 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 %} + {% 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 %} + {% endif %} + {% endfor %} std::unique_ptr<base::Value> Serialize() const; {{resolve_type(type).pass_type}} Clone() const; @@ -86,34 +53,34 @@ class HEADLESS_EXPORT {{type.id}} { public: enum { kNoFieldsSet = 0, - {% set count = 0 %} - {% for property in type.properties %} - {% if not(property.optional) %} - {% set count = count + 1 %} + {% 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 %} + {% 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 not(property.optional) %}k{{property.name | to_title_case}}Set | {%endif %} + {% endfor %}0) }; - {% for property in type.properties %} - {% if property.optional %} + {% 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 %} + {% 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 %} + {% endif %} - {% endfor %} + {% endfor %} {{resolve_type(type).pass_type}} Build() { static_assert(STATE == kAllRequiredFieldsSet, "all required fields should have been set"); return std::move(result_); @@ -137,22 +104,21 @@ class HEADLESS_EXPORT {{type.id}} { private: {{type.id}}() { } - {% for property in type.properties %} - {% if property.optional %} + {% for property in type.properties %} + {% if property.optional %} base::Optional<{{resolve_type(property).type}}> {{property.name | camelcase_to_hacker_style}}_; - {% else %} + {% else %} {{resolve_type(property).type}} {{property.name | camelcase_to_hacker_style}}_; - {% endif %} - {% endfor %} + {% endif %} + {% endfor %} DISALLOW_COPY_AND_ASSIGN({{type.id}}); }; - {% endfor %} +{% endfor %} } // namespace {{domain.domain | camelcase_to_hacker_style}} -{% endfor %} } // namespace headless -#endif // HEADLESS_PUBLIC_DOMAINS_TYPES_H_ +#endif // HEADLESS_PUBLIC_DEVTOOLS_DOMAINS_TYPES_{{domain.domain | camelcase_to_hacker_style | upper}}_H_ diff --git a/chromium/headless/lib/browser/headless_browser_impl.cc b/chromium/headless/lib/browser/headless_browser_impl.cc index 98fcac84ed3..c1647257fd3 100644 --- a/chromium/headless/lib/browser/headless_browser_impl.cc +++ b/chromium/headless/lib/browser/headless_browser_impl.cc @@ -18,7 +18,7 @@ #include "headless/lib/browser/headless_browser_context_impl.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/browser/headless_window_parenting_client.h" #include "headless/lib/headless_content_main_delegate.h" #include "ui/aura/env.h" #include "ui/aura/window_tree_host.h" @@ -123,8 +123,8 @@ void HeadlessBrowserImpl::RunOnStartCallback() { aura::WindowTreeHost::Create(gfx::Rect(options()->window_size))); window_tree_host_->InitHost(); - window_tree_client_.reset( - new HeadlessWindowTreeClient(window_tree_host_->window())); + window_parenting_client_.reset( + new HeadlessWindowParentingClient(window_tree_host_->window())); on_start_callback_.Run(this); on_start_callback_ = base::Callback<void(HeadlessBrowser*)>(); diff --git a/chromium/headless/lib/browser/headless_browser_impl.h b/chromium/headless/lib/browser/headless_browser_impl.h index a31391816c0..74a4b6c29cf 100644 --- a/chromium/headless/lib/browser/headless_browser_impl.h +++ b/chromium/headless/lib/browser/headless_browser_impl.h @@ -20,7 +20,7 @@ namespace aura { class WindowTreeHost; namespace client { -class WindowTreeClient; +class WindowParentingClient; } } @@ -78,7 +78,7 @@ class HeadlessBrowserImpl : public HeadlessBrowser { // is used for all web contents. We should probably use one // window per web contents, but additional investigation is needed. std::unique_ptr<aura::WindowTreeHost> window_tree_host_; - std::unique_ptr<aura::client::WindowTreeClient> window_tree_client_; + std::unique_ptr<aura::client::WindowParentingClient> window_parenting_client_; std::unordered_map<std::string, std::unique_ptr<HeadlessBrowserContextImpl>> browser_contexts_; diff --git a/chromium/headless/lib/browser/headless_browser_manifest_overlay_template.json b/chromium/headless/lib/browser/headless_browser_manifest_overlay_template.json new file mode 100644 index 00000000000..d5e3e9943c0 --- /dev/null +++ b/chromium/headless/lib/browser/headless_browser_manifest_overlay_template.json @@ -0,0 +1,10 @@ +{ + "name": "content_browser", + "interface_provider_specs": { + "navigation:frame": { + "provides": { + "renderer": [] + } + } + } +} diff --git a/chromium/headless/lib/browser/headless_content_browser_client.cc b/chromium/headless/lib/browser/headless_content_browser_client.cc index 4b59e918e5c..1bfe88ff8cb 100644 --- a/chromium/headless/lib/browser/headless_content_browser_client.cc +++ b/chromium/headless/lib/browser/headless_content_browser_client.cc @@ -5,20 +5,30 @@ #include "headless/lib/browser/headless_content_browser_client.h" #include <memory> +#include <unordered_set> #include "base/callback.h" +#include "base/json/json_reader.h" #include "base/memory/ptr_util.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" +#include "content/public/common/service_names.mojom.h" +#include "headless/grit/headless_lib_resources.h" #include "headless/lib/browser/headless_browser_context_impl.h" #include "headless/lib/browser/headless_browser_impl.h" #include "headless/lib/browser/headless_browser_main_parts.h" #include "headless/lib/browser/headless_devtools_manager_delegate.h" +#include "ui/base/resource/resource_bundle.h" namespace headless { +namespace { +const char kCapabilityPath[] = + "interface_provider_specs.navigation:frame.provides.renderer"; +} // namespace + HeadlessContentBrowserClient::HeadlessContentBrowserClient( HeadlessBrowserImpl* browser) : browser_(browser) {} @@ -49,4 +59,31 @@ HeadlessContentBrowserClient::GetDevToolsManagerDelegate() { return new HeadlessDevToolsManagerDelegate(browser_->GetWeakPtr()); } +std::unique_ptr<base::Value> +HeadlessContentBrowserClient::GetServiceManifestOverlay( + const std::string& name) { + if (name != content::mojom::kBrowserServiceName || + browser_->options()->mojo_service_names.empty()) + return nullptr; + + base::StringPiece manifest_template = + ui::ResourceBundle::GetSharedInstance().GetRawDataResource( + IDR_HEADLESS_BROWSER_MANIFEST_OVERLAY_TEMPLATE); + std::unique_ptr<base::Value> manifest = + base::JSONReader::Read(manifest_template); + + // Add mojo_service_names to renderer capability specified in options. + base::DictionaryValue* manifest_dictionary = nullptr; + CHECK(manifest->GetAsDictionary(&manifest_dictionary)); + + base::ListValue* capability_list = nullptr; + CHECK(manifest_dictionary->GetList(kCapabilityPath, &capability_list)); + + for (std::string service_name : browser_->options()->mojo_service_names) { + capability_list->AppendString(service_name); + } + + return manifest; +} + } // namespace headless diff --git a/chromium/headless/lib/browser/headless_content_browser_client.h b/chromium/headless/lib/browser/headless_content_browser_client.h index 0bd58b12246..8e9651e9d21 100644 --- a/chromium/headless/lib/browser/headless_content_browser_client.h +++ b/chromium/headless/lib/browser/headless_content_browser_client.h @@ -24,6 +24,8 @@ class HeadlessContentBrowserClient : public content::ContentBrowserClient { void OverrideWebkitPrefs(content::RenderViewHost* render_view_host, content::WebPreferences* prefs) override; content::DevToolsManagerDelegate* GetDevToolsManagerDelegate() override; + std::unique_ptr<base::Value> GetServiceManifestOverlay( + const std::string& name) override; private: HeadlessBrowserImpl* browser_; // Not owned. diff --git a/chromium/headless/lib/browser/headless_devtools_client_impl.cc b/chromium/headless/lib/browser/headless_devtools_client_impl.cc index a669e1e5b1d..833b9dc49af 100644 --- a/chromium/headless/lib/browser/headless_devtools_client_impl.cc +++ b/chromium/headless/lib/browser/headless_devtools_client_impl.cc @@ -35,7 +35,6 @@ HeadlessDevToolsClientImpl::HeadlessDevToolsClientImpl() accessibility_domain_(this), animation_domain_(this), application_cache_domain_(this), - browser_domain_(this), cache_storage_domain_(this), console_domain_(this), css_domain_(this), @@ -61,8 +60,8 @@ HeadlessDevToolsClientImpl::HeadlessDevToolsClientImpl() runtime_domain_(this), security_domain_(this), service_worker_domain_(this), + target_domain_(this), tracing_domain_(this), - worker_domain_(this), browser_main_thread_(content::BrowserThread::GetTaskRunnerForThread( content::BrowserThread::UI)), weak_ptr_factory_(this) {} @@ -181,10 +180,6 @@ application_cache::Domain* HeadlessDevToolsClientImpl::GetApplicationCache() { return &application_cache_domain_; } -browser::Domain* HeadlessDevToolsClientImpl::GetBrowser() { - return &browser_domain_; -} - cache_storage::Domain* HeadlessDevToolsClientImpl::GetCacheStorage() { return &cache_storage_domain_; } @@ -285,12 +280,12 @@ service_worker::Domain* HeadlessDevToolsClientImpl::GetServiceWorker() { return &service_worker_domain_; } -tracing::Domain* HeadlessDevToolsClientImpl::GetTracing() { - return &tracing_domain_; +target::Domain* HeadlessDevToolsClientImpl::GetTarget() { + return &target_domain_; } -worker::Domain* HeadlessDevToolsClientImpl::GetWorker() { - return &worker_domain_; +tracing::Domain* HeadlessDevToolsClientImpl::GetTracing() { + return &tracing_domain_; } template <typename CallbackType> diff --git a/chromium/headless/lib/browser/headless_devtools_client_impl.h b/chromium/headless/lib/browser/headless_devtools_client_impl.h index 619825d28ba..3ee800b13e8 100644 --- a/chromium/headless/lib/browser/headless_devtools_client_impl.h +++ b/chromium/headless/lib/browser/headless_devtools_client_impl.h @@ -10,37 +10,36 @@ #include "base/memory/weak_ptr.h" #include "base/single_thread_task_runner.h" #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/browser.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/log.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/devtools/domains/accessibility.h" +#include "headless/public/devtools/domains/animation.h" +#include "headless/public/devtools/domains/application_cache.h" +#include "headless/public/devtools/domains/cache_storage.h" +#include "headless/public/devtools/domains/console.h" +#include "headless/public/devtools/domains/css.h" +#include "headless/public/devtools/domains/database.h" +#include "headless/public/devtools/domains/debugger.h" +#include "headless/public/devtools/domains/device_orientation.h" +#include "headless/public/devtools/domains/dom.h" +#include "headless/public/devtools/domains/dom_debugger.h" +#include "headless/public/devtools/domains/dom_storage.h" +#include "headless/public/devtools/domains/emulation.h" +#include "headless/public/devtools/domains/heap_profiler.h" +#include "headless/public/devtools/domains/indexeddb.h" +#include "headless/public/devtools/domains/input.h" +#include "headless/public/devtools/domains/inspector.h" +#include "headless/public/devtools/domains/io.h" +#include "headless/public/devtools/domains/layer_tree.h" +#include "headless/public/devtools/domains/log.h" +#include "headless/public/devtools/domains/memory.h" +#include "headless/public/devtools/domains/network.h" +#include "headless/public/devtools/domains/page.h" +#include "headless/public/devtools/domains/profiler.h" +#include "headless/public/devtools/domains/rendering.h" +#include "headless/public/devtools/domains/runtime.h" +#include "headless/public/devtools/domains/security.h" +#include "headless/public/devtools/domains/service_worker.h" +#include "headless/public/devtools/domains/target.h" +#include "headless/public/devtools/domains/tracing.h" #include "headless/public/headless_devtools_client.h" #include "headless/public/internal/message_dispatcher.h" @@ -67,7 +66,6 @@ class HeadlessDevToolsClientImpl : public HeadlessDevToolsClient, accessibility::Domain* GetAccessibility() override; animation::Domain* GetAnimation() override; application_cache::Domain* GetApplicationCache() override; - browser::Domain* GetBrowser() override; cache_storage::Domain* GetCacheStorage() override; console::Domain* GetConsole() override; css::Domain* GetCSS() override; @@ -93,8 +91,8 @@ class HeadlessDevToolsClientImpl : public HeadlessDevToolsClient, runtime::Domain* GetRuntime() override; security::Domain* GetSecurity() override; service_worker::Domain* GetServiceWorker() override; + target::Domain* GetTarget() override; tracing::Domain* GetTracing() override; - worker::Domain* GetWorker() override; // content::DevToolstAgentHostClient implementation: void DispatchProtocolMessage(content::DevToolsAgentHost* agent_host, @@ -168,7 +166,6 @@ class HeadlessDevToolsClientImpl : public HeadlessDevToolsClient, accessibility::ExperimentalDomain accessibility_domain_; animation::ExperimentalDomain animation_domain_; application_cache::ExperimentalDomain application_cache_domain_; - browser::ExperimentalDomain browser_domain_; cache_storage::ExperimentalDomain cache_storage_domain_; console::ExperimentalDomain console_domain_; css::ExperimentalDomain css_domain_; @@ -194,8 +191,8 @@ class HeadlessDevToolsClientImpl : public HeadlessDevToolsClient, runtime::ExperimentalDomain runtime_domain_; security::ExperimentalDomain security_domain_; service_worker::ExperimentalDomain service_worker_domain_; + target::ExperimentalDomain target_domain_; tracing::ExperimentalDomain tracing_domain_; - worker::ExperimentalDomain worker_domain_; scoped_refptr<base::SingleThreadTaskRunner> browser_main_thread_; base::WeakPtrFactory<HeadlessDevToolsClientImpl> weak_ptr_factory_; diff --git a/chromium/headless/lib/browser/headless_devtools_manager_delegate.cc b/chromium/headless/lib/browser/headless_devtools_manager_delegate.cc index a53e8c773df..0c82f334ab8 100644 --- a/chromium/headless/lib/browser/headless_devtools_manager_delegate.cc +++ b/chromium/headless/lib/browser/headless_devtools_manager_delegate.cc @@ -15,7 +15,7 @@ #include "headless/lib/browser/headless_browser_context_impl.h" #include "headless/lib/browser/headless_browser_impl.h" #include "headless/lib/browser/headless_web_contents_impl.h" -#include "headless/public/domains/browser.h" +#include "headless/public/devtools/domains/target.h" #include "ui/base/resource/resource_bundle.h" namespace headless { @@ -23,13 +23,13 @@ namespace headless { HeadlessDevToolsManagerDelegate::HeadlessDevToolsManagerDelegate( base::WeakPtr<HeadlessBrowserImpl> browser) : browser_(std::move(browser)), default_browser_context_(nullptr) { - command_map_["Browser.createTarget"] = + command_map_["Target.createTarget"] = &HeadlessDevToolsManagerDelegate::CreateTarget; - command_map_["Browser.closeTarget"] = + command_map_["Target.closeTarget"] = &HeadlessDevToolsManagerDelegate::CloseTarget; - command_map_["Browser.createBrowserContext"] = + command_map_["Target.createBrowserContext"] = &HeadlessDevToolsManagerDelegate::CreateBrowserContext; - command_map_["Browser.disposeBrowserContext"] = + command_map_["Target.disposeBrowserContext"] = &HeadlessDevToolsManagerDelegate::DisposeBrowserContext; } @@ -103,7 +103,7 @@ std::unique_ptr<base::Value> HeadlessDevToolsManagerDelegate::CreateTarget( .SetWindowSize(gfx::Size(width, height)) .Build()); - return browser::CreateTargetResult::Builder() + return target::CreateTargetResult::Builder() .SetTargetId(web_contents_impl->GetDevToolsAgentHostId()) .Build() ->Serialize(); @@ -122,7 +122,7 @@ std::unique_ptr<base::Value> HeadlessDevToolsManagerDelegate::CloseTarget( web_contents->Close(); success = true; } - return browser::CloseTargetResult::Builder() + return target::CloseTargetResult::Builder() .SetSuccess(success) .Build() ->Serialize(); @@ -135,7 +135,7 @@ HeadlessDevToolsManagerDelegate::CreateBrowserContext( browser_->CreateBrowserContextBuilder().Build(); std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue()); - return browser::CreateBrowserContextResult::Builder() + return target::CreateBrowserContextResult::Builder() .SetBrowserContextId(browser_context->Id()) .Build() ->Serialize(); @@ -158,7 +158,7 @@ HeadlessDevToolsManagerDelegate::DisposeBrowserContext( context->Close(); } - return browser::DisposeBrowserContextResult::Builder() + return target::DisposeBrowserContextResult::Builder() .SetSuccess(success) .Build() ->Serialize(); diff --git a/chromium/headless/lib/browser/headless_screen.cc b/chromium/headless/lib/browser/headless_screen.cc index c8be18bfdb1..be86924d2e3 100644 --- a/chromium/headless/lib/browser/headless_screen.cc +++ b/chromium/headless/lib/browser/headless_screen.cc @@ -12,7 +12,6 @@ #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" @@ -37,7 +36,8 @@ HeadlessScreen::~HeadlessScreen() {} aura::WindowTreeHost* HeadlessScreen::CreateHostForPrimaryDisplay() { DCHECK(!host_); - host_ = aura::WindowTreeHost::Create(gfx::Rect(display_.GetSizeInPixel())); + host_ = aura::WindowTreeHost::Create( + gfx::Rect(GetPrimaryDisplay().GetSizeInPixel())); // Some tests don't correctly manage window focus/activation states. // Makes sure InputMethod is default focused so that IME basics can work. host_->GetInputMethod()->OnFocus(); @@ -47,50 +47,59 @@ aura::WindowTreeHost* HeadlessScreen::CreateHostForPrimaryDisplay() { } void HeadlessScreen::SetDeviceScaleFactor(float device_scale_factor) { - gfx::Rect bounds_in_pixel(display_.GetSizeInPixel()); - display_.SetScaleAndBounds(device_scale_factor, bounds_in_pixel); + display::Display display(GetPrimaryDisplay()); + gfx::Rect bounds_in_pixel(display.GetSizeInPixel()); + display.SetScaleAndBounds(device_scale_factor, bounds_in_pixel); + display_list().UpdateDisplay(display); } void HeadlessScreen::SetDisplayRotation(display::Display::Rotation rotation) { - gfx::Rect bounds_in_pixel(display_.GetSizeInPixel()); + display::Display display(GetPrimaryDisplay()); + gfx::Rect bounds_in_pixel(display.GetSizeInPixel()); gfx::Rect new_bounds(bounds_in_pixel); - if (IsRotationPortrait(rotation) != IsRotationPortrait(display_.rotation())) { + if (IsRotationPortrait(rotation) != IsRotationPortrait(display.rotation())) { new_bounds.set_width(bounds_in_pixel.height()); new_bounds.set_height(bounds_in_pixel.width()); } - display_.set_rotation(rotation); - display_.SetScaleAndBounds(display_.device_scale_factor(), new_bounds); + display.set_rotation(rotation); + display.SetScaleAndBounds(display.device_scale_factor(), new_bounds); + display_list().UpdateDisplay(display); host_->SetRootTransform(GetRotationTransform() * GetUIScaleTransform()); } void HeadlessScreen::SetUIScale(float ui_scale) { ui_scale_ = ui_scale; - gfx::Rect bounds_in_pixel(display_.GetSizeInPixel()); + display::Display display(GetPrimaryDisplay()); + gfx::Rect bounds_in_pixel(display.GetSizeInPixel()); gfx::Rect new_bounds = gfx::ToNearestRect( gfx::ScaleRect(gfx::RectF(bounds_in_pixel), 1.0f / ui_scale)); - display_.SetScaleAndBounds(display_.device_scale_factor(), new_bounds); + display.SetScaleAndBounds(display.device_scale_factor(), new_bounds); + display_list().UpdateDisplay(display); host_->SetRootTransform(GetRotationTransform() * GetUIScaleTransform()); } void HeadlessScreen::SetWorkAreaInsets(const gfx::Insets& insets) { - display_.UpdateWorkAreaFromInsets(insets); + display::Display display(GetPrimaryDisplay()); + display.UpdateWorkAreaFromInsets(insets); + display_list().UpdateDisplay(display); } gfx::Transform HeadlessScreen::GetRotationTransform() const { gfx::Transform rotate; - switch (display_.rotation()) { + display::Display display(GetPrimaryDisplay()); + switch (display.rotation()) { case display::Display::ROTATE_0: break; case display::Display::ROTATE_90: - rotate.Translate(display_.bounds().height(), 0); + rotate.Translate(display.bounds().height(), 0); rotate.Rotate(90); break; case display::Display::ROTATE_270: - rotate.Translate(0, display_.bounds().width()); + rotate.Translate(0, display.bounds().width()); rotate.Rotate(270); break; case display::Display::ROTATE_180: - rotate.Translate(display_.bounds().width(), display_.bounds().height()); + rotate.Translate(display.bounds().width(), display.bounds().height()); rotate.Rotate(180); break; } @@ -108,8 +117,10 @@ void HeadlessScreen::OnWindowBoundsChanged(aura::Window* window, const gfx::Rect& old_bounds, const gfx::Rect& new_bounds) { DCHECK_EQ(host_->window(), window); - display_.SetSize(gfx::ScaleToFlooredSize(new_bounds.size(), - display_.device_scale_factor())); + display::Display display(GetPrimaryDisplay()); + display.SetSize(gfx::ScaleToFlooredSize(new_bounds.size(), + display.device_scale_factor())); + display_list().UpdateDisplay(display); } void HeadlessScreen::OnWindowDestroying(aura::Window* window) { @@ -132,42 +143,17 @@ gfx::NativeWindow HeadlessScreen::GetWindowAtScreenPoint( return host_->window()->GetTopWindowContainingPoint(point); } -int HeadlessScreen::GetNumDisplays() const { - return 1; -} - -std::vector<display::Display> HeadlessScreen::GetAllDisplays() const { - return std::vector<display::Display>(1, display_); -} - display::Display HeadlessScreen::GetDisplayNearestWindow( gfx::NativeWindow window) const { - return display_; -} - -display::Display HeadlessScreen::GetDisplayNearestPoint( - const gfx::Point& point) const { - return display_; + return GetPrimaryDisplay(); } -display::Display HeadlessScreen::GetDisplayMatching( - const gfx::Rect& match_rect) const { - return display_; -} - -display::Display HeadlessScreen::GetPrimaryDisplay() const { - return display_; -} - -void HeadlessScreen::AddObserver(display::DisplayObserver* observer) {} - -void HeadlessScreen::RemoveObserver(display::DisplayObserver* observer) {} - HeadlessScreen::HeadlessScreen(const gfx::Rect& screen_bounds) : host_(NULL), ui_scale_(1.0f) { static int64_t synthesized_display_id = 2000; - display_.set_id(synthesized_display_id++); - display_.SetScaleAndBounds(1.0f, screen_bounds); + display::Display display(synthesized_display_id++); + display.SetScaleAndBounds(1.0f, screen_bounds); + ProcessDisplayChanged(display, true /* is_primary */); } } // namespace headless diff --git a/chromium/headless/lib/browser/headless_screen.h b/chromium/headless/lib/browser/headless_screen.h index 962fc9c61b8..ed9e388f079 100644 --- a/chromium/headless/lib/browser/headless_screen.h +++ b/chromium/headless/lib/browser/headless_screen.h @@ -9,7 +9,7 @@ #include "base/macros.h" #include "ui/aura/window_observer.h" #include "ui/display/display.h" -#include "ui/display/screen.h" +#include "ui/display/screen_base.h" namespace gfx { class Insets; @@ -19,13 +19,13 @@ class Transform; namespace aura { class Window; -class WindowTreeClient; +class WindowParentingClient; class WindowTreeHost; } namespace headless { -class HeadlessScreen : public display::Screen, public aura::WindowObserver { +class HeadlessScreen : public display::ScreenBase, public aura::WindowObserver { public: // Creates a display::Screen of the specified size (physical pixels). static HeadlessScreen* Create(const gfx::Size& size); @@ -52,22 +52,12 @@ class HeadlessScreen : public display::Screen, public aura::WindowObserver { gfx::Point GetCursorScreenPoint() override; bool IsWindowUnderCursor(gfx::NativeWindow window) override; gfx::NativeWindow GetWindowAtScreenPoint(const gfx::Point& point) override; - int GetNumDisplays() const 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_; - display::Display display_; float ui_scale_; DISALLOW_COPY_AND_ASSIGN(HeadlessScreen); diff --git a/chromium/headless/lib/browser/headless_web_contents_impl.cc b/chromium/headless/lib/browser/headless_web_contents_impl.cc index 20865a3da63..dfdf31b30db 100644 --- a/chromium/headless/lib/browser/headless_web_contents_impl.cc +++ b/chromium/headless/lib/browser/headless_web_contents_impl.cc @@ -13,6 +13,8 @@ #include "base/memory/weak_ptr.h" #include "base/strings/utf_string_conversions.h" #include "base/trace_event/trace_event.h" +#include "components/security_state/content/content_utils.h" +#include "components/security_state/core/security_state.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/devtools_agent_host.h" #include "content/public/browser/navigation_handle.h" @@ -23,12 +25,13 @@ #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_delegate.h" #include "content/public/common/bindings_policy.h" +#include "content/public/common/origin_util.h" #include "content/public/renderer/render_frame.h" #include "headless/lib/browser/headless_browser_context_impl.h" #include "headless/lib/browser/headless_browser_impl.h" #include "headless/lib/browser/headless_browser_main_parts.h" #include "headless/lib/browser/headless_devtools_client_impl.h" -#include "services/shell/public/cpp/interface_registry.h" +#include "services/service_manager/public/cpp/interface_registry.h" #include "ui/aura/window.h" namespace headless { @@ -80,6 +83,21 @@ class HeadlessWebContentsImpl::Delegate : public content::WebContentsDelegate { browser_context_->RegisterWebContents(std::move(web_contents)); } + // Return the security style of the given |web_contents|, populating + // |security_style_explanations| to explain why the SecurityStyle was chosen. + blink::WebSecurityStyle GetSecurityStyle( + content::WebContents* web_contents, + content::SecurityStyleExplanations* security_style_explanations) + override { + security_state::SecurityInfo security_info; + security_state::GetSecurityInfo( + security_state::GetVisibleSecurityState(web_contents), + false /* used_policy_installed_certificate */, + base::Bind(&content::IsOriginSecure), &security_info); + return security_state::GetSecurityStyle(security_info, + security_style_explanations); + } + private: HeadlessBrowserContextImpl* browser_context_; // Not owned. DISALLOW_COPY_AND_ASSIGN(Delegate); @@ -154,7 +172,7 @@ void HeadlessWebContentsImpl::RenderFrameCreated( content::BINDINGS_POLICY_HEADLESS); } - shell::InterfaceRegistry* interface_registry = + service_manager::InterfaceRegistry* interface_registry = render_frame_host->GetInterfaceRegistry(); for (const MojoService& service : mojo_services_) { diff --git a/chromium/headless/lib/browser/headless_window_parenting_client.cc b/chromium/headless/lib/browser/headless_window_parenting_client.cc new file mode 100644 index 00000000000..e27da578895 --- /dev/null +++ b/chromium/headless/lib/browser/headless_window_parenting_client.cc @@ -0,0 +1,28 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "headless/lib/browser/headless_window_parenting_client.h" + +#include "ui/aura/window.h" + +namespace headless { + +HeadlessWindowParentingClient::HeadlessWindowParentingClient( + aura::Window* root_window) + : root_window_(root_window) { + aura::client::SetWindowParentingClient(root_window_, this); +} + +HeadlessWindowParentingClient::~HeadlessWindowParentingClient() { + aura::client::SetWindowParentingClient(root_window_, nullptr); +} + +aura::Window* HeadlessWindowParentingClient::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_parenting_client.h b/chromium/headless/lib/browser/headless_window_parenting_client.h new file mode 100644 index 00000000000..1694080fd3f --- /dev/null +++ b/chromium/headless/lib/browser/headless_window_parenting_client.h @@ -0,0 +1,31 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef HEADLESS_LIB_BROWSER_HEADLESS_WINDOW_PARENTING_CLIENT_H_ +#define HEADLESS_LIB_BROWSER_HEADLESS_WINDOW_PARENTING_CLIENT_H_ + +#include "base/macros.h" +#include "ui/aura/client/window_parenting_client.h" + +namespace headless { + +class HeadlessWindowParentingClient + : public aura::client::WindowParentingClient { + public: + explicit HeadlessWindowParentingClient(aura::Window* root_window); + ~HeadlessWindowParentingClient() 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(HeadlessWindowParentingClient); +}; + +} // namespace headless + +#endif // HEADLESS_LIB_BROWSER_HEADLESS_WINDOW_PARENTING_CLIENT_H_ diff --git a/chromium/headless/lib/browser/headless_window_tree_client.cc b/chromium/headless/lib/browser/headless_window_tree_client.cc deleted file mode 100644 index be797ce3032..00000000000 --- a/chromium/headless/lib/browser/headless_window_tree_client.cc +++ /dev/null @@ -1,27 +0,0 @@ -// 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 deleted file mode 100644 index c64e3daf92b..00000000000 --- a/chromium/headless/lib/browser/headless_window_tree_client.h +++ /dev/null @@ -1,30 +0,0 @@ -// 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/embedder_mojo_browsertest.cc b/chromium/headless/lib/embedder_mojo_browsertest.cc index a8338e04801..3fb4ed1a5f5 100644 --- a/chromium/headless/lib/embedder_mojo_browsertest.cc +++ b/chromium/headless/lib/embedder_mojo_browsertest.cc @@ -5,14 +5,15 @@ #include <memory> #include "base/optional.h" #include "base/path_service.h" +#include "base/strings/string_piece.h" #include "base/strings/stringprintf.h" #include "base/threading/thread_restrictions.h" #include "content/public/test/browser_test.h" #include "headless/grit/headless_browsertest_resources.h" #include "headless/lib/embedder_test.mojom.h" -#include "headless/public/domains/network.h" -#include "headless/public/domains/page.h" -#include "headless/public/domains/runtime.h" +#include "headless/public/devtools/domains/network.h" +#include "headless/public/devtools/domains/page.h" +#include "headless/public/devtools/domains/runtime.h" #include "headless/public/headless_browser.h" #include "headless/public/headless_devtools_client.h" #include "headless/public/headless_devtools_target.h" @@ -49,6 +50,13 @@ class EmbedderMojoTest : public HeadlessBrowserTest, ~EmbedderMojoTest() override {} + void SetUp() override { + // Set service names before they are used during main thread initialization. + options()->mojo_service_names.insert("embedder_test::TestEmbedderService"); + + HeadlessBrowserTest::SetUp(); + } + void SetUpOnMainThread() override { base::ThreadRestrictions::SetIOAllowed(true); base::FilePath pak_path; diff --git a/chromium/headless/lib/headless_browser_browsertest.cc b/chromium/headless/lib/headless_browser_browsertest.cc index 77796ffe349..0bbf976849c 100644 --- a/chromium/headless/lib/headless_browser_browsertest.cc +++ b/chromium/headless/lib/headless_browser_browsertest.cc @@ -10,8 +10,8 @@ #include "base/strings/stringprintf.h" #include "base/threading/thread_restrictions.h" #include "content/public/test/browser_test.h" -#include "headless/public/domains/network.h" -#include "headless/public/domains/page.h" +#include "headless/public/devtools/domains/network.h" +#include "headless/public/devtools/domains/page.h" #include "headless/public/headless_browser.h" #include "headless/public/headless_devtools_client.h" #include "headless/public/headless_devtools_target.h" diff --git a/chromium/headless/lib/headless_devtools_client_browsertest.cc b/chromium/headless/lib/headless_devtools_client_browsertest.cc index 5188e74c404..26252d8a7fd 100644 --- a/chromium/headless/lib/headless_devtools_client_browsertest.cc +++ b/chromium/headless/lib/headless_devtools_client_browsertest.cc @@ -9,11 +9,11 @@ #include "content/public/browser/web_contents.h" #include "content/public/test/browser_test.h" #include "headless/lib/browser/headless_web_contents_impl.h" -#include "headless/public/domains/browser.h" -#include "headless/public/domains/emulation.h" -#include "headless/public/domains/network.h" -#include "headless/public/domains/page.h" -#include "headless/public/domains/runtime.h" +#include "headless/public/devtools/domains/emulation.h" +#include "headless/public/devtools/domains/network.h" +#include "headless/public/devtools/domains/page.h" +#include "headless/public/devtools/domains/runtime.h" +#include "headless/public/devtools/domains/target.h" #include "headless/public/headless_browser.h" #include "headless/public/headless_devtools_client.h" #include "headless/public/headless_devtools_target.h" @@ -218,25 +218,25 @@ class HeadlessDevToolsClientExperimentalTest HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsClientExperimentalTest); -class BrowserDomainCreateAndDeletePageTest +class TargetDomainCreateAndDeletePageTest : public HeadlessAsyncDevTooledBrowserTest { void RunDevTooledTest() override { EXPECT_TRUE(embedded_test_server()->Start()); EXPECT_EQ(1u, GetAllWebContents(browser()).size()); - devtools_client_->GetBrowser()->GetExperimental()->CreateTarget( - browser::CreateTargetParams::Builder() + devtools_client_->GetTarget()->GetExperimental()->CreateTarget( + target::CreateTargetParams::Builder() .SetUrl(embedded_test_server()->GetURL("/hello.html").spec()) .SetWidth(1) .SetHeight(1) .Build(), - base::Bind(&BrowserDomainCreateAndDeletePageTest::OnCreateTargetResult, + base::Bind(&TargetDomainCreateAndDeletePageTest::OnCreateTargetResult, base::Unretained(this))); } void OnCreateTargetResult( - std::unique_ptr<browser::CreateTargetResult> result) { + std::unique_ptr<target::CreateTargetResult> result) { EXPECT_EQ(2u, GetAllWebContents(browser()).size()); HeadlessWebContentsImpl* contents = HeadlessWebContentsImpl::From( @@ -246,81 +246,81 @@ class BrowserDomainCreateAndDeletePageTest ->GetViewBounds() .size()); - devtools_client_->GetBrowser()->GetExperimental()->CloseTarget( - browser::CloseTargetParams::Builder() + devtools_client_->GetTarget()->GetExperimental()->CloseTarget( + target::CloseTargetParams::Builder() .SetTargetId(result->GetTargetId()) .Build(), - base::Bind(&BrowserDomainCreateAndDeletePageTest::OnCloseTargetResult, + base::Bind(&TargetDomainCreateAndDeletePageTest::OnCloseTargetResult, base::Unretained(this))); } - void OnCloseTargetResult(std::unique_ptr<browser::CloseTargetResult> result) { + void OnCloseTargetResult(std::unique_ptr<target::CloseTargetResult> result) { EXPECT_TRUE(result->GetSuccess()); EXPECT_EQ(1u, GetAllWebContents(browser()).size()); FinishAsynchronousTest(); } }; -HEADLESS_ASYNC_DEVTOOLED_TEST_F(BrowserDomainCreateAndDeletePageTest); +HEADLESS_ASYNC_DEVTOOLED_TEST_F(TargetDomainCreateAndDeletePageTest); -class BrowserDomainCreateAndDeleteBrowserContextTest +class TargetDomainCreateAndDeleteBrowserContextTest : public HeadlessAsyncDevTooledBrowserTest { void RunDevTooledTest() override { EXPECT_TRUE(embedded_test_server()->Start()); EXPECT_EQ(1u, GetAllWebContents(browser()).size()); - devtools_client_->GetBrowser()->GetExperimental()->CreateBrowserContext( - browser::CreateBrowserContextParams::Builder().Build(), - base::Bind(&BrowserDomainCreateAndDeleteBrowserContextTest:: + devtools_client_->GetTarget()->GetExperimental()->CreateBrowserContext( + target::CreateBrowserContextParams::Builder().Build(), + base::Bind(&TargetDomainCreateAndDeleteBrowserContextTest:: OnCreateContextResult, base::Unretained(this))); } void OnCreateContextResult( - std::unique_ptr<browser::CreateBrowserContextResult> result) { + std::unique_ptr<target::CreateBrowserContextResult> result) { browser_context_id_ = result->GetBrowserContextId(); - devtools_client_->GetBrowser()->GetExperimental()->CreateTarget( - browser::CreateTargetParams::Builder() + devtools_client_->GetTarget()->GetExperimental()->CreateTarget( + target::CreateTargetParams::Builder() .SetUrl(embedded_test_server()->GetURL("/hello.html").spec()) .SetBrowserContextId(result->GetBrowserContextId()) .SetWidth(1) .SetHeight(1) .Build(), - base::Bind(&BrowserDomainCreateAndDeleteBrowserContextTest:: + base::Bind(&TargetDomainCreateAndDeleteBrowserContextTest:: OnCreateTargetResult, base::Unretained(this))); } void OnCreateTargetResult( - std::unique_ptr<browser::CreateTargetResult> result) { + std::unique_ptr<target::CreateTargetResult> result) { EXPECT_EQ(2u, GetAllWebContents(browser()).size()); - devtools_client_->GetBrowser()->GetExperimental()->CloseTarget( - browser::CloseTargetParams::Builder() + devtools_client_->GetTarget()->GetExperimental()->CloseTarget( + target::CloseTargetParams::Builder() .SetTargetId(result->GetTargetId()) .Build(), - base::Bind(&BrowserDomainCreateAndDeleteBrowserContextTest:: + base::Bind(&TargetDomainCreateAndDeleteBrowserContextTest:: OnCloseTargetResult, base::Unretained(this))); } - void OnCloseTargetResult(std::unique_ptr<browser::CloseTargetResult> result) { + void OnCloseTargetResult(std::unique_ptr<target::CloseTargetResult> result) { EXPECT_EQ(1u, GetAllWebContents(browser()).size()); EXPECT_TRUE(result->GetSuccess()); - devtools_client_->GetBrowser()->GetExperimental()->DisposeBrowserContext( - browser::DisposeBrowserContextParams::Builder() + devtools_client_->GetTarget()->GetExperimental()->DisposeBrowserContext( + target::DisposeBrowserContextParams::Builder() .SetBrowserContextId(browser_context_id_) .Build(), - base::Bind(&BrowserDomainCreateAndDeleteBrowserContextTest:: + base::Bind(&TargetDomainCreateAndDeleteBrowserContextTest:: OnDisposeBrowserContextResult, base::Unretained(this))); } void OnDisposeBrowserContextResult( - std::unique_ptr<browser::DisposeBrowserContextResult> result) { + std::unique_ptr<target::DisposeBrowserContextResult> result) { EXPECT_TRUE(result->GetSuccess()); FinishAsynchronousTest(); } @@ -329,73 +329,73 @@ class BrowserDomainCreateAndDeleteBrowserContextTest std::string browser_context_id_; }; -HEADLESS_ASYNC_DEVTOOLED_TEST_F(BrowserDomainCreateAndDeleteBrowserContextTest); +HEADLESS_ASYNC_DEVTOOLED_TEST_F(TargetDomainCreateAndDeleteBrowserContextTest); -class BrowserDomainDisposeContextFailsIfInUse +class TargetDomainDisposeContextFailsIfInUse : public HeadlessAsyncDevTooledBrowserTest { void RunDevTooledTest() override { EXPECT_TRUE(embedded_test_server()->Start()); EXPECT_EQ(1u, GetAllWebContents(browser()).size()); - devtools_client_->GetBrowser()->GetExperimental()->CreateBrowserContext( - browser::CreateBrowserContextParams::Builder().Build(), - base::Bind(&BrowserDomainDisposeContextFailsIfInUse::OnContextCreated, + devtools_client_->GetTarget()->GetExperimental()->CreateBrowserContext( + target::CreateBrowserContextParams::Builder().Build(), + base::Bind(&TargetDomainDisposeContextFailsIfInUse::OnContextCreated, base::Unretained(this))); } void OnContextCreated( - std::unique_ptr<browser::CreateBrowserContextResult> result) { + std::unique_ptr<target::CreateBrowserContextResult> result) { context_id_ = result->GetBrowserContextId(); - devtools_client_->GetBrowser()->GetExperimental()->CreateTarget( - browser::CreateTargetParams::Builder() + devtools_client_->GetTarget()->GetExperimental()->CreateTarget( + target::CreateTargetParams::Builder() .SetUrl(embedded_test_server()->GetURL("/hello.html").spec()) .SetBrowserContextId(context_id_) .Build(), base::Bind( - &BrowserDomainDisposeContextFailsIfInUse::OnCreateTargetResult, + &TargetDomainDisposeContextFailsIfInUse::OnCreateTargetResult, base::Unretained(this))); } void OnCreateTargetResult( - std::unique_ptr<browser::CreateTargetResult> result) { + std::unique_ptr<target::CreateTargetResult> result) { page_id_ = result->GetTargetId(); - devtools_client_->GetBrowser()->GetExperimental()->DisposeBrowserContext( - browser::DisposeBrowserContextParams::Builder() + devtools_client_->GetTarget()->GetExperimental()->DisposeBrowserContext( + target::DisposeBrowserContextParams::Builder() .SetBrowserContextId(context_id_) .Build(), - base::Bind(&BrowserDomainDisposeContextFailsIfInUse:: + base::Bind(&TargetDomainDisposeContextFailsIfInUse:: OnDisposeBrowserContextResult, base::Unretained(this))); } void OnDisposeBrowserContextResult( - std::unique_ptr<browser::DisposeBrowserContextResult> result) { + std::unique_ptr<target::DisposeBrowserContextResult> result) { EXPECT_FALSE(result->GetSuccess()); // Close the page and try again. - devtools_client_->GetBrowser()->GetExperimental()->CloseTarget( - browser::CloseTargetParams::Builder().SetTargetId(page_id_).Build(), + devtools_client_->GetTarget()->GetExperimental()->CloseTarget( + target::CloseTargetParams::Builder().SetTargetId(page_id_).Build(), base::Bind( - &BrowserDomainDisposeContextFailsIfInUse::OnCloseTargetResult, + &TargetDomainDisposeContextFailsIfInUse::OnCloseTargetResult, base::Unretained(this))); } - void OnCloseTargetResult(std::unique_ptr<browser::CloseTargetResult> result) { + void OnCloseTargetResult(std::unique_ptr<target::CloseTargetResult> result) { EXPECT_TRUE(result->GetSuccess()); - devtools_client_->GetBrowser()->GetExperimental()->DisposeBrowserContext( - browser::DisposeBrowserContextParams::Builder() + devtools_client_->GetTarget()->GetExperimental()->DisposeBrowserContext( + target::DisposeBrowserContextParams::Builder() .SetBrowserContextId(context_id_) .Build(), - base::Bind(&BrowserDomainDisposeContextFailsIfInUse:: + base::Bind(&TargetDomainDisposeContextFailsIfInUse:: OnDisposeBrowserContextResult2, base::Unretained(this))); } void OnDisposeBrowserContextResult2( - std::unique_ptr<browser::DisposeBrowserContextResult> result) { + std::unique_ptr<target::DisposeBrowserContextResult> result) { EXPECT_TRUE(result->GetSuccess()); FinishAsynchronousTest(); } @@ -405,34 +405,34 @@ class BrowserDomainDisposeContextFailsIfInUse std::string page_id_; }; -HEADLESS_ASYNC_DEVTOOLED_TEST_F(BrowserDomainDisposeContextFailsIfInUse); +HEADLESS_ASYNC_DEVTOOLED_TEST_F(TargetDomainDisposeContextFailsIfInUse); -class BrowserDomainCreateTwoContexts : public HeadlessAsyncDevTooledBrowserTest, - public browser::ExperimentalObserver { +class TargetDomainCreateTwoContexts : public HeadlessAsyncDevTooledBrowserTest, + public target::ExperimentalObserver { public: void RunDevTooledTest() override { EXPECT_TRUE(embedded_test_server()->Start()); - devtools_client_->GetBrowser()->GetExperimental()->AddObserver(this); - devtools_client_->GetBrowser()->GetExperimental()->CreateBrowserContext( - browser::CreateBrowserContextParams::Builder().Build(), - base::Bind(&BrowserDomainCreateTwoContexts::OnContextOneCreated, + devtools_client_->GetTarget()->GetExperimental()->AddObserver(this); + devtools_client_->GetTarget()->GetExperimental()->CreateBrowserContext( + target::CreateBrowserContextParams::Builder().Build(), + base::Bind(&TargetDomainCreateTwoContexts::OnContextOneCreated, base::Unretained(this))); - devtools_client_->GetBrowser()->GetExperimental()->CreateBrowserContext( - browser::CreateBrowserContextParams::Builder().Build(), - base::Bind(&BrowserDomainCreateTwoContexts::OnContextTwoCreated, + devtools_client_->GetTarget()->GetExperimental()->CreateBrowserContext( + target::CreateBrowserContextParams::Builder().Build(), + base::Bind(&TargetDomainCreateTwoContexts::OnContextTwoCreated, base::Unretained(this))); } void OnContextOneCreated( - std::unique_ptr<browser::CreateBrowserContextResult> result) { + std::unique_ptr<target::CreateBrowserContextResult> result) { context_id_one_ = result->GetBrowserContextId(); MaybeCreatePages(); } void OnContextTwoCreated( - std::unique_ptr<browser::CreateBrowserContextResult> result) { + std::unique_ptr<target::CreateBrowserContextResult> result) { context_id_two_ = result->GetBrowserContextId(); MaybeCreatePages(); } @@ -441,31 +441,31 @@ class BrowserDomainCreateTwoContexts : public HeadlessAsyncDevTooledBrowserTest, if (context_id_one_.empty() || context_id_two_.empty()) return; - devtools_client_->GetBrowser()->GetExperimental()->CreateTarget( - browser::CreateTargetParams::Builder() + devtools_client_->GetTarget()->GetExperimental()->CreateTarget( + target::CreateTargetParams::Builder() .SetUrl(embedded_test_server()->GetURL("/hello.html").spec()) .SetBrowserContextId(context_id_one_) .Build(), - base::Bind(&BrowserDomainCreateTwoContexts::OnCreateTargetOneResult, + base::Bind(&TargetDomainCreateTwoContexts::OnCreateTargetOneResult, base::Unretained(this))); - devtools_client_->GetBrowser()->GetExperimental()->CreateTarget( - browser::CreateTargetParams::Builder() + devtools_client_->GetTarget()->GetExperimental()->CreateTarget( + target::CreateTargetParams::Builder() .SetUrl(embedded_test_server()->GetURL("/hello.html").spec()) .SetBrowserContextId(context_id_two_) .Build(), - base::Bind(&BrowserDomainCreateTwoContexts::OnCreateTargetTwoResult, + base::Bind(&TargetDomainCreateTwoContexts::OnCreateTargetTwoResult, base::Unretained(this))); } void OnCreateTargetOneResult( - std::unique_ptr<browser::CreateTargetResult> result) { + std::unique_ptr<target::CreateTargetResult> result) { page_id_one_ = result->GetTargetId(); MaybeTestIsolation(); } void OnCreateTargetTwoResult( - std::unique_ptr<browser::CreateTargetResult> result) { + std::unique_ptr<target::CreateTargetResult> result) { page_id_two_ = result->GetTargetId(); MaybeTestIsolation(); } @@ -474,28 +474,34 @@ class BrowserDomainCreateTwoContexts : public HeadlessAsyncDevTooledBrowserTest, if (page_id_one_.empty() || page_id_two_.empty()) return; - devtools_client_->GetBrowser()->GetExperimental()->Attach( - browser::AttachParams::Builder().SetTargetId(page_id_one_).Build(), - base::Bind(&BrowserDomainCreateTwoContexts::OnAttachedOne, + devtools_client_->GetTarget()->GetExperimental()->AttachToTarget( + target::AttachToTargetParams::Builder() + .SetTargetId(page_id_one_) + .Build(), + base::Bind(&TargetDomainCreateTwoContexts::OnAttachedToTargetOne, base::Unretained(this))); - devtools_client_->GetBrowser()->GetExperimental()->Attach( - browser::AttachParams::Builder().SetTargetId(page_id_two_).Build(), - base::Bind(&BrowserDomainCreateTwoContexts::OnAttachedTwo, + devtools_client_->GetTarget()->GetExperimental()->AttachToTarget( + target::AttachToTargetParams::Builder() + .SetTargetId(page_id_two_) + .Build(), + base::Bind(&TargetDomainCreateTwoContexts::OnAttachedToTargetTwo, base::Unretained(this))); } - void OnAttachedOne(std::unique_ptr<browser::AttachResult> result) { - devtools_client_->GetBrowser()->GetExperimental()->SendMessage( - browser::SendMessageParams::Builder() + void OnAttachedToTargetOne( + std::unique_ptr<target::AttachToTargetResult> result) { + devtools_client_->GetTarget()->GetExperimental()->SendMessageToTarget( + target::SendMessageToTargetParams::Builder() .SetTargetId(page_id_one_) .SetMessage("{\"id\":101, \"method\": \"Page.enable\"}") .Build()); } - void OnAttachedTwo(std::unique_ptr<browser::AttachResult> result) { - devtools_client_->GetBrowser()->GetExperimental()->SendMessage( - browser::SendMessageParams::Builder() + void OnAttachedToTargetTwo( + std::unique_ptr<target::AttachToTargetResult> result) { + devtools_client_->GetTarget()->GetExperimental()->SendMessageToTarget( + target::SendMessageToTargetParams::Builder() .SetTargetId(page_id_two_) .SetMessage("{\"id\":102, \"method\": \"Page.enable\"}") .Build()); @@ -505,8 +511,8 @@ class BrowserDomainCreateTwoContexts : public HeadlessAsyncDevTooledBrowserTest, if (!page_one_loaded_ || !page_two_loaded_) return; - devtools_client_->GetBrowser()->GetExperimental()->SendMessage( - browser::SendMessageParams::Builder() + devtools_client_->GetTarget()->GetExperimental()->SendMessageToTarget( + target::SendMessageToTargetParams::Builder() .SetTargetId(page_id_one_) .SetMessage("{\"id\":201, \"method\": \"Runtime.evaluate\", " "\"params\": {\"expression\": " @@ -514,8 +520,8 @@ class BrowserDomainCreateTwoContexts : public HeadlessAsyncDevTooledBrowserTest, .Build()); } - void OnDispatchMessage( - const browser::DispatchMessageParams& params) override { + void OnReceivedMessageFromTarget( + const target::ReceivedMessageFromTargetParams& params) override { std::unique_ptr<base::Value> message = base::JSONReader::Read(params.GetMessage(), base::JSON_PARSE_RFC); const base::DictionaryValue* message_dict; @@ -540,9 +546,10 @@ class BrowserDomainCreateTwoContexts : public HeadlessAsyncDevTooledBrowserTest, return; std::string value; if (params.GetTargetId() == page_id_one_) { - // TODO(alexclarke): Make some better bindings for Browser.sendMessage. - devtools_client_->GetBrowser()->GetExperimental()->SendMessage( - browser::SendMessageParams::Builder() + // TODO(alexclarke): Make some better bindings + // for Target.SendMessageToTarget. + devtools_client_->GetTarget()->GetExperimental()->SendMessageToTarget( + target::SendMessageToTargetParams::Builder() .SetTargetId(page_id_two_) .SetMessage("{\"id\":202, \"method\": \"Runtime.evaluate\", " "\"params\": {\"expression\": " @@ -552,48 +559,48 @@ class BrowserDomainCreateTwoContexts : public HeadlessAsyncDevTooledBrowserTest, result_dict->GetString("value", &value)) { EXPECT_EQ("", value) << "Page 2 should not share cookies from page one"; - devtools_client_->GetBrowser()->GetExperimental()->CloseTarget( - browser::CloseTargetParams::Builder() + devtools_client_->GetTarget()->GetExperimental()->CloseTarget( + target::CloseTargetParams::Builder() .SetTargetId(page_id_one_) .Build(), - base::Bind(&BrowserDomainCreateTwoContexts::OnCloseTarget, + base::Bind(&TargetDomainCreateTwoContexts::OnCloseTarget, base::Unretained(this))); - devtools_client_->GetBrowser()->GetExperimental()->CloseTarget( - browser::CloseTargetParams::Builder() + devtools_client_->GetTarget()->GetExperimental()->CloseTarget( + target::CloseTargetParams::Builder() .SetTargetId(page_id_two_) .Build(), - base::Bind(&BrowserDomainCreateTwoContexts::OnCloseTarget, + base::Bind(&TargetDomainCreateTwoContexts::OnCloseTarget, base::Unretained(this))); - devtools_client_->GetBrowser()->GetExperimental()->RemoveObserver(this); + devtools_client_->GetTarget()->GetExperimental()->RemoveObserver(this); } } } - void OnCloseTarget(std::unique_ptr<browser::CloseTargetResult> result) { + void OnCloseTarget(std::unique_ptr<target::CloseTargetResult> result) { page_close_count_++; if (page_close_count_ < 2) return; - devtools_client_->GetBrowser()->GetExperimental()->DisposeBrowserContext( - browser::DisposeBrowserContextParams::Builder() + devtools_client_->GetTarget()->GetExperimental()->DisposeBrowserContext( + target::DisposeBrowserContextParams::Builder() .SetBrowserContextId(context_id_one_) .Build(), - base::Bind(&BrowserDomainCreateTwoContexts::OnCloseContext, + base::Bind(&TargetDomainCreateTwoContexts::OnCloseContext, base::Unretained(this))); - devtools_client_->GetBrowser()->GetExperimental()->DisposeBrowserContext( - browser::DisposeBrowserContextParams::Builder() + devtools_client_->GetTarget()->GetExperimental()->DisposeBrowserContext( + target::DisposeBrowserContextParams::Builder() .SetBrowserContextId(context_id_two_) .Build(), - base::Bind(&BrowserDomainCreateTwoContexts::OnCloseContext, + base::Bind(&TargetDomainCreateTwoContexts::OnCloseContext, base::Unretained(this))); } void OnCloseContext( - std::unique_ptr<browser::DisposeBrowserContextResult> result) { + std::unique_ptr<target::DisposeBrowserContextResult> result) { EXPECT_TRUE(result->GetSuccess()); if (++context_closed_count_ < 2) return; @@ -612,7 +619,7 @@ class BrowserDomainCreateTwoContexts : public HeadlessAsyncDevTooledBrowserTest, int context_closed_count_ = 0; }; -HEADLESS_ASYNC_DEVTOOLED_TEST_F(BrowserDomainCreateTwoContexts); +HEADLESS_ASYNC_DEVTOOLED_TEST_F(TargetDomainCreateTwoContexts); class HeadlessDevToolsNavigationControlTest : public HeadlessAsyncDevTooledBrowserTest, diff --git a/chromium/headless/lib/headless_web_contents_browsertest.cc b/chromium/headless/lib/headless_web_contents_browsertest.cc index 0fd928371ed..51f519c9055 100644 --- a/chromium/headless/lib/headless_web_contents_browsertest.cc +++ b/chromium/headless/lib/headless_web_contents_browsertest.cc @@ -7,7 +7,8 @@ #include <vector> #include "content/public/test/browser_test.h" -#include "headless/public/domains/page.h" +#include "headless/public/devtools/domains/page.h" +#include "headless/public/devtools/domains/security.h" #include "headless/public/headless_browser.h" #include "headless/public/headless_devtools_client.h" #include "headless/public/headless_web_contents.h" @@ -74,4 +75,25 @@ class HeadlessWebContentsScreenshotTest HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessWebContentsScreenshotTest); +class HeadlessWebContentsSecurityTest + : public HeadlessAsyncDevTooledBrowserTest, + public security::ExperimentalObserver { + public: + void RunDevTooledTest() override { + devtools_client_->GetSecurity()->GetExperimental()->AddObserver(this); + devtools_client_->GetSecurity()->GetExperimental()->Enable( + security::EnableParams::Builder().Build()); + } + + void OnSecurityStateChanged( + const security::SecurityStateChangedParams& params) override { + EXPECT_EQ(security::SecurityState::NEUTRAL, params.GetSecurityState()); + EXPECT_TRUE(params.HasExplanations()); + + FinishAsynchronousTest(); + } +}; + +HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessWebContentsSecurityTest); + } // namespace headless diff --git a/chromium/headless/lib/resources/headless_lib_resources.grd b/chromium/headless/lib/resources/headless_lib_resources.grd index 3137ec64834..9a0e2b0e33c 100644 --- a/chromium/headless/lib/resources/headless_lib_resources.grd +++ b/chromium/headless/lib/resources/headless_lib_resources.grd @@ -10,6 +10,7 @@ <release seq="1"> <includes> <include name="IDR_HEADLESS_LIB_DEVTOOLS_DISCOVERY_PAGE" file="devtools_discovery_page.html" type="BINDATA" /> + <include name="IDR_HEADLESS_BROWSER_MANIFEST_OVERLAY_TEMPLATE" file="../browser/headless_browser_manifest_overlay_template.json" type="BINDATA" /> </includes> </release> </grit> diff --git a/chromium/headless/public/domains/README.md b/chromium/headless/public/domains/README.md index a3f8898c5b6..c0689e4002a 100644 --- a/chromium/headless/public/domains/README.md +++ b/chromium/headless/public/domains/README.md @@ -1,3 +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). +out-directory, e.g., out/Debug/gen/headless/public/devtools/domains, or in +[Code Search](https://code.google.com/p/chromium/codesearch#search/&q=f%3Agen/headless/public/devtools/domains&sq=package:chromium&type=cs). diff --git a/chromium/headless/public/domains/types_unittest.cc b/chromium/headless/public/domains/types_unittest.cc index bf59b01151a..e5c0efa7e5a 100644 --- a/chromium/headless/public/domains/types_unittest.cc +++ b/chromium/headless/public/domains/types_unittest.cc @@ -3,7 +3,9 @@ // found in the LICENSE file. #include "base/json/json_reader.h" -#include "headless/public/domains/types.h" +#include "headless/public/devtools/domains/accessibility.h" +#include "headless/public/devtools/domains/memory.h" +#include "headless/public/devtools/domains/page.h" #include "testing/gtest/include/gtest/gtest.h" namespace headless { @@ -24,9 +26,11 @@ TEST(TypesTest, IntegerPropertyParseError) { std::unique_ptr<base::Value> object = base::JSONReader::Read(json); EXPECT_TRUE(object); +#if DCHECK_IS_ON() ErrorReporter errors; EXPECT_FALSE(page::NavigateToHistoryEntryParams::Parse(*object, &errors)); EXPECT_TRUE(errors.HasErrors()); +#endif // DCHECK_IS_ON() } TEST(TypesTest, BooleanProperty) { @@ -48,10 +52,12 @@ TEST(TypesTest, BooleanPropertyParseError) { std::unique_ptr<base::Value> object = base::JSONReader::Read(json); EXPECT_TRUE(object); +#if DCHECK_IS_ON() ErrorReporter errors; EXPECT_FALSE(memory::SetPressureNotificationsSuppressedParams::Parse( *object, &errors)); EXPECT_TRUE(errors.HasErrors()); +#endif // DCHECK_IS_ON() } TEST(TypesTest, DoubleProperty) { @@ -70,9 +76,11 @@ TEST(TypesTest, DoublePropertyParseError) { std::unique_ptr<base::Value> object = base::JSONReader::Read(json); EXPECT_TRUE(object); +#if DCHECK_IS_ON() ErrorReporter errors; EXPECT_FALSE(page::SetGeolocationOverrideParams::Parse(*object, &errors)); EXPECT_TRUE(errors.HasErrors()); +#endif // DCHECK_IS_ON() } TEST(TypesTest, StringProperty) { @@ -91,9 +99,11 @@ TEST(TypesTest, StringPropertyParseError) { std::unique_ptr<base::Value> object = base::JSONReader::Read(json); EXPECT_TRUE(object); +#if DCHECK_IS_ON() ErrorReporter errors; EXPECT_FALSE(page::NavigateParams::Parse(*object, &errors)); EXPECT_TRUE(errors.HasErrors()); +#endif // DCHECK_IS_ON() } TEST(TypesTest, EnumProperty) { @@ -114,9 +124,11 @@ TEST(TypesTest, EnumPropertyParseError) { std::unique_ptr<base::Value> object = base::JSONReader::Read(json); EXPECT_TRUE(object); +#if DCHECK_IS_ON() ErrorReporter errors; EXPECT_FALSE(runtime::RemoteObject::Parse(*object, &errors)); EXPECT_TRUE(errors.HasErrors()); +#endif // DCHECK_IS_ON() } TEST(TypesTest, ArrayProperty) { @@ -146,9 +158,11 @@ TEST(TypesTest, ArrayPropertyParseError) { std::unique_ptr<base::Value> object = base::JSONReader::Read(json); EXPECT_TRUE(object); +#if DCHECK_IS_ON() ErrorReporter errors; EXPECT_FALSE(dom::QuerySelectorAllResult::Parse(*object, &errors)); EXPECT_TRUE(errors.HasErrors()); +#endif // DCHECK_IS_ON() } TEST(TypesTest, ObjectProperty) { @@ -173,9 +187,11 @@ TEST(TypesTest, ObjectPropertyParseError) { std::unique_ptr<base::Value> object = base::JSONReader::Read(json); EXPECT_TRUE(object); +#if DCHECK_IS_ON() ErrorReporter errors; EXPECT_FALSE(runtime::EvaluateResult::Parse(*object, &errors)); EXPECT_TRUE(errors.HasErrors()); +#endif // DCHECK_IS_ON() } TEST(TypesTest, AnyProperty) { diff --git a/chromium/headless/public/headless_browser.cc b/chromium/headless/public/headless_browser.cc index cbfbc5e400e..2b4270bd591 100644 --- a/chromium/headless/public/headless_browser.cc +++ b/chromium/headless/public/headless_browser.cc @@ -82,6 +82,11 @@ Builder& Builder::SetGLImplementation(const std::string& gl_implementation) { return *this; } +Builder& Builder::AddMojoServiceName(const std::string& mojo_service_name) { + options_.mojo_service_names.insert(mojo_service_name); + return *this; +} + Builder& Builder::SetUserDataDir(const base::FilePath& user_data_dir) { options_.user_data_dir = user_data_dir; return *this; diff --git a/chromium/headless/public/headless_browser.h b/chromium/headless/public/headless_browser.h index 954b79e0db4..021e91064fb 100644 --- a/chromium/headless/public/headless_browser.h +++ b/chromium/headless/public/headless_browser.h @@ -8,6 +8,7 @@ #include <memory> #include <string> #include <unordered_map> +#include <unordered_set> #include <vector> #include "base/callback.h" @@ -115,6 +116,10 @@ struct HeadlessBrowser::Options { // string can be used to disable GL rendering (e.g., WebGL support). std::string gl_implementation; + // Names of mojo services exposed by the browser to the renderer. These + // services will be added to the browser's service manifest. + std::unordered_set<std::string> mojo_service_names; + // Default per-context options, can be specialized on per-context basis. std::string user_agent; @@ -159,6 +164,7 @@ class HeadlessBrowser::Options::Builder { Builder& SetSingleProcessMode(bool single_process_mode); Builder& SetDisableSandbox(bool disable_sandbox); Builder& SetGLImplementation(const std::string& gl_implementation); + Builder& AddMojoServiceName(const std::string& mojo_service_name); // Per-context settings. diff --git a/chromium/headless/public/headless_devtools_client.h b/chromium/headless/public/headless_devtools_client.h index 36e6ea3877a..573336af792 100644 --- a/chromium/headless/public/headless_devtools_client.h +++ b/chromium/headless/public/headless_devtools_client.h @@ -21,9 +21,6 @@ class Domain; namespace application_cache { class Domain; } -namespace browser { -class Domain; -} namespace cache_storage { class Domain; } @@ -99,10 +96,10 @@ class Domain; namespace service_worker { class Domain; } -namespace tracing { +namespace target { class Domain; } -namespace worker { +namespace tracing { class Domain; } @@ -121,7 +118,6 @@ class HEADLESS_EXPORT HeadlessDevToolsClient { virtual accessibility::Domain* GetAccessibility() = 0; virtual animation::Domain* GetAnimation() = 0; virtual application_cache::Domain* GetApplicationCache() = 0; - virtual browser::Domain* GetBrowser() = 0; virtual cache_storage::Domain* GetCacheStorage() = 0; virtual console::Domain* GetConsole() = 0; virtual css::Domain* GetCSS() = 0; @@ -147,8 +143,8 @@ class HEADLESS_EXPORT HeadlessDevToolsClient { virtual runtime::Domain* GetRuntime() = 0; virtual security::Domain* GetSecurity() = 0; virtual service_worker::Domain* GetServiceWorker() = 0; + virtual target::Domain* GetTarget() = 0; virtual tracing::Domain* GetTracing() = 0; - virtual worker::Domain* GetWorker() = 0; // TODO(skyostil): Add notification for disconnection. diff --git a/chromium/headless/public/headless_shell.h b/chromium/headless/public/headless_shell.h new file mode 100644 index 00000000000..f083d2030c1 --- /dev/null +++ b/chromium/headless/public/headless_shell.h @@ -0,0 +1,18 @@ +// 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_SHELL_H_ +#define HEADLESS_PUBLIC_HEADLESS_SHELL_H_ + +#include "headless/public/headless_export.h" + +namespace headless { + +// Start the Headless Shell application. Intended to be called early in main(). +// Returns the exit code for the process. +int HEADLESS_EXPORT HeadlessShellMain(int argc, const char** argv); + +} // namespace headless + +#endif // HEADLESS_PUBLIC_HEADLESS_SHELL_H_ diff --git a/chromium/headless/public/internal/value_conversions.h b/chromium/headless/public/internal/value_conversions.h index c25a0cd1ce2..4a85a02a464 100644 --- a/chromium/headless/public/internal/value_conversions.h +++ b/chromium/headless/public/internal/value_conversions.h @@ -14,7 +14,7 @@ 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. +// types_DOMAIN.cc after all type-specific ToValueImpls have been defined. template <typename T> std::unique_ptr<base::Value> ToValue(const T& value); diff --git a/chromium/headless/public/util/deterministic_dispatcher.cc b/chromium/headless/public/util/deterministic_dispatcher.cc index 70696eaeaa9..20982db898c 100644 --- a/chromium/headless/public/util/deterministic_dispatcher.cc +++ b/chromium/headless/public/util/deterministic_dispatcher.cc @@ -15,7 +15,8 @@ namespace headless { DeterministicDispatcher::DeterministicDispatcher( scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner) : io_thread_task_runner_(std::move(io_thread_task_runner)), - dispatch_pending_(false) {} + dispatch_pending_(false), + weak_ptr_factory_(this) {} DeterministicDispatcher::~DeterministicDispatcher() {} @@ -63,7 +64,7 @@ void DeterministicDispatcher::MaybeDispatchJobLocked() { io_thread_task_runner_->PostTask( FROM_HERE, base::Bind(&DeterministicDispatcher::MaybeDispatchJobOnIOThreadTask, - base::Unretained(this))); + weak_ptr_factory_.GetWeakPtr())); } void DeterministicDispatcher::MaybeDispatchJobOnIOThreadTask() { @@ -72,8 +73,10 @@ void DeterministicDispatcher::MaybeDispatchJobOnIOThreadTask() { { base::AutoLock lock(lock_); - CHECK(!pending_requests_.empty()); dispatch_pending_ = false; + // If the job got deleted, |pending_requests_| may be empty. + if (pending_requests_.empty()) + return; job = pending_requests_.front(); StatusMap::const_iterator it = ready_status_map_.find(job); // Bail out if the oldest job is not be ready for dispatch yet. diff --git a/chromium/headless/public/util/deterministic_dispatcher.h b/chromium/headless/public/util/deterministic_dispatcher.h index c2e73b61710..0fea383b9cc 100644 --- a/chromium/headless/public/util/deterministic_dispatcher.h +++ b/chromium/headless/public/util/deterministic_dispatcher.h @@ -10,6 +10,7 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" #include "base/single_thread_task_runner.h" #include "base/synchronization/lock.h" #include "headless/public/util/url_request_dispatcher.h" @@ -54,6 +55,8 @@ class DeterministicDispatcher : public URLRequestDispatcher { // |io_thread_task_runner_| bool dispatch_pending_; + base::WeakPtrFactory<DeterministicDispatcher> weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(DeterministicDispatcher); }; diff --git a/chromium/headless/public/util/deterministic_dispatcher_test.cc b/chromium/headless/public/util/deterministic_dispatcher_test.cc index b4c1c1fe089..24cede8b2e9 100644 --- a/chromium/headless/public/util/deterministic_dispatcher_test.cc +++ b/chromium/headless/public/util/deterministic_dispatcher_test.cc @@ -110,4 +110,20 @@ TEST_F(DeterministicDispatcherTest, "id: 3 err: -123", "id: 4 OK")); } +TEST_F(DeterministicDispatcherTest, JobKilled) { + std::vector<std::string> notifications; + { + std::unique_ptr<FakeManagedDispatchURLRequestJob> job( + new FakeManagedDispatchURLRequestJob(deterministic_dispatcher_.get(), 1, + ¬ifications)); + + job->Kill(); + } + + EXPECT_TRUE(notifications.empty()); + + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(notifications.empty()); +} + } // namespace headless diff --git a/chromium/headless/public/util/dom_tree_extractor.cc b/chromium/headless/public/util/dom_tree_extractor.cc new file mode 100644 index 00000000000..6e4154c44e4 --- /dev/null +++ b/chromium/headless/public/util/dom_tree_extractor.cc @@ -0,0 +1,109 @@ +// 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/dom_tree_extractor.h" + +#include "base/bind.h" +#include "base/json/json_writer.h" +#include "headless/public/headless_devtools_client.h" + +namespace headless { + +DomTreeExtractor::DomTreeExtractor(HeadlessDevToolsClient* devtools_client) + : work_in_progress_(false), + devtools_client_(devtools_client), + weak_factory_(this) {} + +DomTreeExtractor::~DomTreeExtractor() {} + +void DomTreeExtractor::ExtractDomTree( + const std::vector<std::string>& css_style_whitelist, + DomResultCB callback) { + DCHECK(!work_in_progress_); + work_in_progress_ = true; + + callback_ = std::move(callback); + + devtools_client_->GetDOM()->GetDocument( + dom::GetDocumentParams::Builder().SetDepth(-1).SetPierce(true).Build(), + base::Bind(&DomTreeExtractor::OnDocumentFetched, + weak_factory_.GetWeakPtr())); + + devtools_client_->GetCSS()->GetExperimental()->GetLayoutTreeAndStyles( + css::GetLayoutTreeAndStylesParams::Builder() + .SetComputedStyleWhitelist(css_style_whitelist) + .Build(), + base::Bind(&DomTreeExtractor::OnLayoutTreeAndStylesFetched, + weak_factory_.GetWeakPtr())); +} + +void DomTreeExtractor::OnDocumentFetched( + std::unique_ptr<dom::GetDocumentResult> result) { + dom_tree_.document_result_ = std::move(result); + MaybeExtractDomTree(); +} + +void DomTreeExtractor::OnLayoutTreeAndStylesFetched( + std::unique_ptr<css::GetLayoutTreeAndStylesResult> result) { + dom_tree_.layout_tree_and_styles_result_ = std::move(result); + MaybeExtractDomTree(); +} + +void DomTreeExtractor::MaybeExtractDomTree() { + if (dom_tree_.document_result_ && dom_tree_.layout_tree_and_styles_result_) { + EnumerateNodes(dom_tree_.document_result_->GetRoot()); + ExtractLayoutTreeNodes(); + ExtractComputedStyles(); + + work_in_progress_ = false; + + callback_.Run(std::move(dom_tree_)); + } +} + +void DomTreeExtractor::EnumerateNodes(const dom::Node* node) { + // Allocate an index and record the node pointer. + size_t index = dom_tree_.node_id_to_index_.size(); + dom_tree_.node_id_to_index_[node->GetNodeId()] = index; + dom_tree_.dom_nodes_.push_back(node); + + if (node->HasContentDocument()) + EnumerateNodes(node->GetContentDocument()); + + if (node->HasChildren()) { + for (const std::unique_ptr<dom::Node>& child : *node->GetChildren()) { + EnumerateNodes(child.get()); + } + } +} + +void DomTreeExtractor::ExtractLayoutTreeNodes() { + dom_tree_.layout_tree_nodes_.reserve( + dom_tree_.layout_tree_and_styles_result_->GetLayoutTreeNodes()->size()); + + for (const std::unique_ptr<css::LayoutTreeNode>& layout_node : + *dom_tree_.layout_tree_and_styles_result_->GetLayoutTreeNodes()) { + std::unordered_map<NodeId, size_t>::const_iterator it = + dom_tree_.node_id_to_index_.find(layout_node->GetNodeId()); + DCHECK(it != dom_tree_.node_id_to_index_.end()); + dom_tree_.layout_tree_nodes_.push_back(layout_node.get()); + } +} + +void DomTreeExtractor::ExtractComputedStyles() { + dom_tree_.computed_styles_.reserve( + dom_tree_.layout_tree_and_styles_result_->GetComputedStyles()->size()); + + for (const std::unique_ptr<css::ComputedStyle>& computed_style : + *dom_tree_.layout_tree_and_styles_result_->GetComputedStyles()) { + dom_tree_.computed_styles_.push_back(computed_style.get()); + } +} + +DomTreeExtractor::DomTree::DomTree() {} +DomTreeExtractor::DomTree::~DomTree() {} + +DomTreeExtractor::DomTree::DomTree(DomTree&& other) = default; + +} // namespace headless diff --git a/chromium/headless/public/util/dom_tree_extractor.h b/chromium/headless/public/util/dom_tree_extractor.h new file mode 100644 index 00000000000..dcd08bfe917 --- /dev/null +++ b/chromium/headless/public/util/dom_tree_extractor.h @@ -0,0 +1,88 @@ +// 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_DOM_TREE_EXTRACTOR_H_ +#define HEADLESS_PUBLIC_UTIL_DOM_TREE_EXTRACTOR_H_ + +#include <unordered_map> +#include <vector> + +#include "base/macros.h" +#include "headless/public/devtools/domains/css.h" +#include "headless/public/devtools/domains/dom.h" + +namespace headless { +class HeadlessDevToolsClient; + +// A utility class for extracting information from the DOM via DevTools. In +// addition, it also extracts details of bounding boxes and layout text (NB the +// exact layout should not be regarded as stable, it's subject to change without +// notice). +class DomTreeExtractor { + public: + explicit DomTreeExtractor(HeadlessDevToolsClient* devtools_client); + ~DomTreeExtractor(); + + using NodeId = int; + using Index = size_t; + + class DomTree { + public: + DomTree(); + DomTree(DomTree&& other); + ~DomTree(); + + // Flattened dom tree. The root node is always the first entry. + std::vector<const dom::Node*> dom_nodes_; + + // Map of node IDs to indexes into |dom_nodes_|. + std::unordered_map<NodeId, Index> node_id_to_index_; + + std::vector<const css::LayoutTreeNode*> layout_tree_nodes_; + + std::vector<const css::ComputedStyle*> computed_styles_; + + private: + friend class DomTreeExtractor; + + // Owns the raw pointers in |dom_nodes_|. + std::unique_ptr<dom::GetDocumentResult> document_result_; + + // Owns the raw pointers in |layout_tree_nodes_|. + std::unique_ptr<css::GetLayoutTreeAndStylesResult> + layout_tree_and_styles_result_; + + DISALLOW_COPY_AND_ASSIGN(DomTree); + }; + + using DomResultCB = base::Callback<void(DomTree)>; + + // Extracts all nodes from the DOM. This is an asynchronous operation and + // it's an error to call ExtractDom while a previous operation is in flight. + void ExtractDomTree(const std::vector<std::string>& css_style_whitelist, + DomResultCB callback); + + private: + void OnDocumentFetched(std::unique_ptr<dom::GetDocumentResult> result); + + void OnLayoutTreeAndStylesFetched( + std::unique_ptr<css::GetLayoutTreeAndStylesResult> result); + + void MaybeExtractDomTree(); + void EnumerateNodes(const dom::Node* node); + void ExtractLayoutTreeNodes(); + void ExtractComputedStyles(); + + DomResultCB callback_; + DomTree dom_tree_; + bool work_in_progress_; + HeadlessDevToolsClient* devtools_client_; // NOT OWNED + base::WeakPtrFactory<DomTreeExtractor> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(DomTreeExtractor); +}; + +} // namespace headless + +#endif // HEADLESS_PUBLIC_UTIL_DOM_TREE_EXTRACTOR_H_ diff --git a/chromium/headless/public/util/dom_tree_extractor_browsertest.cc b/chromium/headless/public/util/dom_tree_extractor_browsertest.cc new file mode 100644 index 00000000000..57b6a54869f --- /dev/null +++ b/chromium/headless/public/util/dom_tree_extractor_browsertest.cc @@ -0,0 +1,845 @@ +// 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/dom_tree_extractor.h" + +#include <memory> +#include "base/json/json_reader.h" +#include "base/json/json_writer.h" +#include "base/strings/string_util.h" +#include "content/public/browser/render_widget_host_view.h" +#include "content/public/browser/web_contents.h" +#include "content/public/test/browser_test.h" +#include "headless/lib/browser/headless_web_contents_impl.h" +#include "headless/public/devtools/domains/emulation.h" +#include "headless/public/devtools/domains/network.h" +#include "headless/public/devtools/domains/page.h" +#include "headless/public/headless_browser.h" +#include "headless/public/headless_devtools_client.h" +#include "headless/public/headless_devtools_target.h" +#include "headless/test/headless_browser_test.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "url/gurl.h" + +namespace headless { + +namespace { + +std::string NormaliseJSON(const std::string& json) { + std::unique_ptr<base::Value> parsed_json = base::JSONReader::Read(json); + DCHECK(parsed_json); + std::string normalized_json; + base::JSONWriter::WriteWithOptions( + *parsed_json, base::JSONWriter::OPTIONS_PRETTY_PRINT, &normalized_json); + return normalized_json; +} + +} // namespace + +class DomTreeExtractorBrowserTest : public HeadlessAsyncDevTooledBrowserTest, + public page::Observer { + public: + void RunDevTooledTest() override { + EXPECT_TRUE(embedded_test_server()->Start()); + devtools_client_->GetPage()->AddObserver(this); + devtools_client_->GetPage()->Enable(); + devtools_client_->GetPage()->Navigate( + embedded_test_server()->GetURL("/dom_tree_test.html").spec()); + } + + void OnLoadEventFired(const page::LoadEventFiredParams& params) override { + devtools_client_->GetPage()->RemoveObserver(this); + + extractor_.reset(new DomTreeExtractor(devtools_client_.get())); + + std::vector<std::string> css_whitelist = { + "color", "display", "font-style", "margin-left", + "margin-right", "margin-top", "margin-bottom"}; + extractor_->ExtractDomTree( + css_whitelist, + base::Bind(&DomTreeExtractorBrowserTest::OnDomTreeExtracted, + base::Unretained(this))); + } + + void OnDomTreeExtracted(DomTreeExtractor::DomTree dom_tree) { + GURL::Replacements replace_port; + replace_port.SetPortStr(""); + + std::vector<std::unique_ptr<base::DictionaryValue>> dom_nodes( + dom_tree.dom_nodes_.size()); + + // For convenience flatten the dom tree into an array. + for (size_t i = 0; i < dom_tree.dom_nodes_.size(); i++) { + dom::Node* node = const_cast<dom::Node*>(dom_tree.dom_nodes_[i]); + + dom_nodes[i].reset( + static_cast<base::DictionaryValue*>(node->Serialize().release())); + + // Convert child & content document pointers into indexes. + if (node->HasChildren()) { + std::unique_ptr<base::ListValue> children(new base::ListValue()); + for (const std::unique_ptr<dom::Node>& child : *node->GetChildren()) { + children->AppendInteger( + dom_tree.node_id_to_index_[child->GetNodeId()]); + } + dom_nodes[i]->Set("childIndices", std::move(children)); + dom_nodes[i]->Remove("children", nullptr); + } + + if (node->HasContentDocument()) { + dom_nodes[i]->SetInteger( + "contentDocumentIndex", + dom_tree + .node_id_to_index_[node->GetContentDocument()->GetNodeId()]); + dom_nodes[i]->Remove("contentDocument", nullptr); + } + + dom_nodes[i]->Remove("childNodeCount", nullptr); + + // Frame IDs are random. + if (dom_nodes[i]->HasKey("frameId")) + dom_nodes[i]->SetString("frameId", "?"); + + // Ports are random. + std::string url; + if (dom_nodes[i]->GetString("baseURL", &url)) { + dom_nodes[i]->SetString( + "baseURL", GURL(url).ReplaceComponents(replace_port).spec()); + } + + if (dom_nodes[i]->GetString("documentURL", &url)) { + dom_nodes[i]->SetString( + "documentURL", GURL(url).ReplaceComponents(replace_port).spec()); + } + } + + // Merge LayoutTreeNode data into the dictionaries. + for (const css::LayoutTreeNode* layout_node : dom_tree.layout_tree_nodes_) { + auto it = dom_tree.node_id_to_index_.find(layout_node->GetNodeId()); + ASSERT_TRUE(it != dom_tree.node_id_to_index_.end()); + + base::DictionaryValue* node_dict = dom_nodes[it->second].get(); + node_dict->Set("boundingBox", layout_node->GetBoundingBox()->Serialize()); + + if (layout_node->HasLayoutText()) + node_dict->SetString("layoutText", layout_node->GetLayoutText()); + + if (layout_node->HasStyleIndex()) + node_dict->SetInteger("styleIndex", layout_node->GetStyleIndex()); + + if (layout_node->HasInlineTextNodes()) { + std::unique_ptr<base::ListValue> inline_text_nodes( + new base::ListValue()); + for (const std::unique_ptr<css::InlineTextBox>& inline_text_box : + *layout_node->GetInlineTextNodes()) { + size_t index = inline_text_nodes->GetSize(); + inline_text_nodes->Set(index, inline_text_box->Serialize()); + } + node_dict->Set("inlineTextNodes", std::move(inline_text_nodes)); + } + } + + std::vector<std::unique_ptr<base::DictionaryValue>> computed_styles( + dom_tree.computed_styles_.size()); + + for (size_t i = 0; i < dom_tree.computed_styles_.size(); i++) { + std::unique_ptr<base::DictionaryValue> style(new base::DictionaryValue()); + for (const auto& style_property : + *dom_tree.computed_styles_[i]->GetProperties()) { + style->SetString(style_property->GetName(), style_property->GetValue()); + } + computed_styles[i] = std::move(style); + } + + const std::vector<std::string> expected_dom_nodes = { + R"raw_string({ + "backendNodeId": 3, + "baseURL": "http://127.0.0.1/dom_tree_test.html", + "boundingBox": { + "height": 600.0, + "width": 800.0, + "x": 0.0, + "y": 0.0 + }, + "childIndices": [ 1 ], + "documentURL": "http://127.0.0.1/dom_tree_test.html", + "localName": "", + "nodeId": 1, + "nodeName": "#document", + "nodeType": 9, + "nodeValue": "", + "xmlVersion": "" + })raw_string", + + R"raw_string({ + "attributes": [ ], + "backendNodeId": 4, + "boundingBox": { + "height": 600.0, + "width": 800.0, + "x": 0.0, + "y": 0.0 + }, + "childIndices": [ 2, 5 ], + "frameId": "?", + "localName": "html", + "nodeId": 2, + "nodeName": "HTML", + "nodeType": 1, + "nodeValue": "", + "styleIndex": 0 + })raw_string", + + R"raw_string({ + "attributes": [ ], + "backendNodeId": 5, + "childIndices": [ 3 ], + "localName": "head", + "nodeId": 3, + "nodeName": "HEAD", + "nodeType": 1, + "nodeValue": "" + })raw_string", + + R"raw_string({ + "attributes": [ ], + "backendNodeId": 6, + "childIndices": [ 4 ], + "localName": "title", + "nodeId": 4, + "nodeName": "TITLE", + "nodeType": 1, + "nodeValue": "" + })raw_string", + + R"raw_string({ + "backendNodeId": 7, + "localName": "", + "nodeId": 5, + "nodeName": "#text", + "nodeType": 3, + "nodeValue": "Hello world!" + })raw_string", + + R"raw_string({ + "attributes": [ ], + "backendNodeId": 8, + "boundingBox": { + "height": 584.0, + "width": 784.0, + "x": 8.0, + "y": 8.0 + }, + "childIndices": [ 6 ], + "localName": "body", + "nodeId": 6, + "nodeName": "BODY", + "nodeType": 1, + "nodeValue": "", + "styleIndex": 1 + })raw_string", + + R"raw_string({ + "attributes": [ "id", "id1" ], + "backendNodeId": 9, + "boundingBox": { + "height": 367.0, + "width": 784.0, + "x": 8.0, + "y": 8.0 + }, + "childIndices": [ 7, 9, 16 ], + "localName": "div", + "nodeId": 7, + "nodeName": "DIV", + "nodeType": 1, + "nodeValue": "", + "styleIndex": 0 + })raw_string", + + R"raw_string({ + "attributes": [ "style", "color: red" ], + "backendNodeId": 10, + "boundingBox": { + "height": 37.0, + "width": 784.0, + "x": 8.0, + "y": 8.0 + }, + "childIndices": [ 8 ], + "localName": "h1", + "nodeId": 8, + "nodeName": "H1", + "nodeType": 1, + "nodeValue": "", + "styleIndex": 2 + })raw_string", + + R"raw_string({ + "backendNodeId": 11, + "boundingBox": { + "height": 36.0, + "width": 143.0, + "x": 8.0, + "y": 8.0 + }, + "inlineTextNodes": [ { + "boundingBox": { + "height": 36.0, + "width": 142.171875, + "x": 8.0, + "y": 8.0 + }, + "numCharacters": 10, + "startCharacterIndex": 0 + } ], + "layoutText": "Some text.", + "localName": "", + "nodeId": 9, + "nodeName": "#text", + "nodeType": 3, + "nodeValue": "Some text.", + "styleIndex": 2 + })raw_string", + + R"raw_string({ + "attributes": [ + "src", "/iframe.html", "width", "400", "height", "200" ], + "backendNodeId": 12, + "boundingBox": { + "height": 205.0, + "width": 404.0, + "x": 8.0, + "y": 66.0 + }, + "childIndices": [ ], + "contentDocumentIndex": 10, + "frameId": "?", + "localName": "iframe", + "nodeId": 10, + "nodeName": "IFRAME", + "nodeType": 1, + "nodeValue": "", + "styleIndex": 4 + })raw_string", + + R"raw_string({ + "backendNodeId": 13, + "baseURL": "http://127.0.0.1/iframe.html", + "childIndices": [ 11 ], + "documentURL": "http://127.0.0.1/iframe.html", + "localName": "", + "nodeId": 11, + "nodeName": "#document", + "nodeType": 9, + "nodeValue": "", + "xmlVersion": "" + })raw_string", + + R"raw_string({ + "attributes": [ ], + "backendNodeId": 14, + "boundingBox": { + "height": 200.0, + "width": 400.0, + "x": 10.0, + "y": 68.0 + }, + "childIndices": [ 12, 13 ], + "frameId": "?", + "localName": "html", + "nodeId": 12, + "nodeName": "HTML", + "nodeType": 1, + "nodeValue": "", + "styleIndex": 0 + })raw_string", + + R"raw_string({ + "attributes": [ ], + "backendNodeId": 15, + "childIndices": [ ], + "localName": "head", + "nodeId": 13, + "nodeName": "HEAD", + "nodeType": 1, + "nodeValue": "" + })raw_string", + + R"raw_string({ + "attributes": [ ], + "backendNodeId": 16, + "boundingBox": { + "height": 171.0, + "width": 384.0, + "x": 18.0, + "y": 76.0 + }, + "childIndices": [ 14 ], + "localName": "body", + "nodeId": 14, + "nodeName": "BODY", + "nodeType": 1, + "nodeValue": "", + "styleIndex": 1 + })raw_string", + + R"raw_string({ + "attributes": [ ], + "backendNodeId": 17, + "boundingBox": { + "height": 37.0, + "width": 384.0, + "x": 18.0, + "y": 76.0 + }, + "childIndices": [ 15 ], + "localName": "h1", + "nodeId": 15, + "nodeName": "H1", + "nodeType": 1, + "nodeValue": "", + "styleIndex": 3 + })raw_string", + + R"raw_string({ + "backendNodeId": 18, + "boundingBox": { + "height": 36.0, + "width": 308.0, + "x": 8.0, + "y": 8.0 + }, + "inlineTextNodes": [ { + "boundingBox": { + "height": 36.0, + "width": 307.734375, + "x": 8.0, + "y": 8.0 + }, + "numCharacters": 22, + "startCharacterIndex": 0 + } ], + "layoutText": "Hello from the iframe!", + "localName": "", + "nodeId": 16, + "nodeName": "#text", + "nodeType": 3, + "nodeValue": "Hello from the iframe!", + "styleIndex": 3 + })raw_string", + + R"raw_string({ + "attributes": [ "id", "id2" ], + "backendNodeId": 19, + "boundingBox": { + "height": 105.0, + "width": 784.0, + "x": 8.0, + "y": 270.0 + }, + "childIndices": [ 17 ], + "localName": "div", + "nodeId": 17, + "nodeName": "DIV", + "nodeType": 1, + "nodeValue": "", + "styleIndex": 0 + })raw_string", + + R"raw_string({ + "attributes": [ "id", "id3" ], + "backendNodeId": 20, + "boundingBox": { + "height": 105.0, + "width": 784.0, + "x": 8.0, + "y": 270.0 + }, + "childIndices": [ 18 ], + "localName": "div", + "nodeId": 18, + "nodeName": "DIV", + "nodeType": 1, + "nodeValue": "", + "styleIndex": 0 + })raw_string", + + R"raw_string({ + "attributes": [ "id", "id4" ], + "backendNodeId": 21, + "boundingBox": { + "height": 105.0, + "width": 784.0, + "x": 8.0, + "y": 270.0 + }, + "childIndices": [ 19, 21, 23, 24 ], + "localName": "div", + "nodeId": 19, + "nodeName": "DIV", + "nodeType": 1, + "nodeValue": "", + "styleIndex": 0 + })raw_string", + + R"raw_string({ + "attributes": [ "href", "https://www.google.com" ], + "backendNodeId": 22, + "boundingBox": { + "height": 18.0, + "width": 53.0, + "x": 8.0, + "y": 270.0 + }, + "childIndices": [ 20 ], + "localName": "a", + "nodeId": 20, + "nodeName": "A", + "nodeType": 1, + "nodeValue": "", + "styleIndex": 5 + })raw_string", + + R"raw_string({ + "backendNodeId": 23, + "boundingBox": { + "height": 18.0, + "width": 53.0, + "x": 8.0, + "y": 270.0 + }, + "inlineTextNodes": [ { + "boundingBox": { + "height": 17.0, + "width": 52.421875, + "x": 8.0, + "y": 270.4375 + }, + "numCharacters": 7, + "startCharacterIndex": 0 + } ], + "layoutText": "Google!", + "localName": "", + "nodeId": 21, + "nodeName": "#text", + "nodeType": 3, + "nodeValue": "Google!", + "styleIndex": 5 + })raw_string", + + R"raw_string({ + "attributes": [ ], + "backendNodeId": 24, + "boundingBox": { + "height": 19.0, + "width": 784.0, + "x": 8.0, + "y": 304.0 + }, + "childIndices": [ 22 ], + "localName": "p", + "nodeId": 22, + "nodeName": "P", + "nodeType": 1, + "nodeValue": "", + "styleIndex": 6 + })raw_string", + + R"raw_string({ + "backendNodeId": 25, + "boundingBox": { + "height": 18.0, + "width": 85.0, + "x": 8.0, + "y": 304.0 + }, + "inlineTextNodes": [ { + "boundingBox": { + "height": 17.0, + "width": 84.84375, + "x": 8.0, + "y": 304.4375 + }, + "numCharacters": 12, + "startCharacterIndex": 0 + } ], + "layoutText": "A paragraph!", + "localName": "", + "nodeId": 23, + "nodeName": "#text", + "nodeType": 3, + "nodeValue": "A paragraph!", + "styleIndex": 6 + })raw_string", + + R"raw_string({ + "attributes": [ ], + "backendNodeId": 26, + "boundingBox": { + "height": 0.0, + "width": 0.0, + "x": 0.0, + "y": 0.0 + }, + "childIndices": [ ], + "inlineTextNodes": [ { + "boundingBox": { + "height": 17.0, + "width": 0.0, + "x": 8.0, + "y": 338.4375 + }, + "numCharacters": 1, + "startCharacterIndex": 0 + } ], + "layoutText": "\n", + "localName": "br", + "nodeId": 24, + "nodeName": "BR", + "nodeType": 1, + "nodeValue": "", + "styleIndex": 4 + })raw_string", + + R"raw_string({ + "attributes": [ "style", "color: green" ], + "backendNodeId": 27, + "boundingBox": { + "height": 19.0, + "width": 784.0, + "x": 8.0, + "y": 356.0 + }, + "childIndices": [ 25, 26, 28 ], + "localName": "div", + "nodeId": 25, + "nodeName": "DIV", + "nodeType": 1, + "nodeValue": "", + "styleIndex": 7 + })raw_string", + + R"raw_string({ + "backendNodeId": 28, + "boundingBox": { + "height": 18.0, + "width": 41.0, + "x": 8.0, + "y": 356.0 + }, + "inlineTextNodes": [ { + "boundingBox": { + "height": 17.0, + "width": 40.4375, + "x": 8.0, + "y": 356.4375 + }, + "numCharacters": 5, + "startCharacterIndex": 0 + } ], + "layoutText": "Some ", + "localName": "", + "nodeId": 26, + "nodeName": "#text", + "nodeType": 3, + "nodeValue": "Some ", + "styleIndex": 7 + })raw_string", + + R"raw_string({ + "attributes": [ ], + "backendNodeId": 29, + "boundingBox": { + "height": 18.0, + "width": 37.0, + "x": 48.0, + "y": 356.0 + }, + "childIndices": [ 27 ], + "localName": "em", + "nodeId": 27, + "nodeName": "EM", + "nodeType": 1, + "nodeValue": "", + "styleIndex": 8 + })raw_string", + + R"raw_string({ + "backendNodeId": 30, + "boundingBox": { + "height": 18.0, + "width": 37.0, + "x": 48.0, + "y": 356.0 + }, + "inlineTextNodes": [ { + "boundingBox": { + "height": 17.0, + "width": 35.828125, + "x": 48.4375, + "y": 356.4375 + }, + "numCharacters": 5, + "startCharacterIndex": 0 + } ], + "layoutText": "green", + "localName": "", + "nodeId": 28, + "nodeName": "#text", + "nodeType": 3, + "nodeValue": "green", + "styleIndex": 8 + })raw_string", + + R"raw_string({ + "backendNodeId": 31, + "boundingBox": { + "height": 18.0, + "width": 41.0, + "x": 84.0, + "y": 356.0 + }, + "inlineTextNodes": [ { + "boundingBox": { + "height": 17.0, + "width": 39.984375, + "x": 84.265625, + "y": 356.4375 + }, + "numCharacters": 8, + "startCharacterIndex": 0 + } ], + "layoutText": " text...", + "localName": "", + "nodeId": 29, + "nodeName": "#text", + "nodeType": 3, + "nodeValue": " text...", + "styleIndex": 7 + })raw_string"}; + + EXPECT_EQ(expected_dom_nodes.size(), dom_nodes.size()); + + for (size_t i = 0; i < dom_nodes.size(); i++) { + std::string result_json; + base::JSONWriter::WriteWithOptions( + *dom_nodes[i], base::JSONWriter::OPTIONS_PRETTY_PRINT, &result_json); + + ASSERT_LT(i, expected_dom_nodes.size()); + EXPECT_EQ(NormaliseJSON(expected_dom_nodes[i]), result_json) << " Node # " + << i; + } + + const std::vector<std::string> expected_styles = { + R"raw_string({ + "color": "rgb(0, 0, 0)", + "display": "block", + "font-style": "normal", + "margin-bottom": "0px", + "margin-left": "0px", + "margin-right": "0px", + "margin-top": "0px" + })raw_string", + + R"raw_string({ + "color": "rgb(0, 0, 0)", + "display": "block", + "font-style": "normal", + "margin-bottom": "8px", + "margin-left": "8px", + "margin-right": "8px", + "margin-top": "8px" + })raw_string", + + R"raw_string({ + "color": "rgb(255, 0, 0)", + "display": "block", + "font-style": "normal", + "margin-bottom": "21.44px", + "margin-left": "0px", + "margin-right": "0px", + "margin-top": "21.44px" + })raw_string", + + R"raw_string({ + "color": "rgb(0, 0, 0)", + "display": "block", + "font-style": "normal", + "margin-bottom": "21.44px", + "margin-left": "0px", + "margin-right": "0px", + "margin-top": "21.44px" + })raw_string", + + R"raw_string({ + "color": "rgb(0, 0, 0)", + "display": "inline", + "font-style": "normal", + "margin-bottom": "0px", + "margin-left": "0px", + "margin-right": "0px", + "margin-top": "0px" + })raw_string", + + R"raw_string({ + "color": "rgb(0, 0, 238)", + "display": "inline", + "font-style": "normal", + "margin-bottom": "0px", + "margin-left": "0px", + "margin-right": "0px", + "margin-top": "0px" + })raw_string", + + R"raw_string({ + "color": "rgb(0, 0, 0)", + "display": "block", + "font-style": "normal", + "margin-bottom": "16px", + "margin-left": "0px", + "margin-right": "0px", + "margin-top": "16px" + })raw_string", + + R"raw_string({ + "color": "rgb(0, 128, 0)", + "display": "block", + "font-style": "normal", + "margin-bottom": "0px", + "margin-left": "0px", + "margin-right": "0px", + "margin-top": "0px" + })raw_string", + + R"raw_string({ + "color": "rgb(0, 128, 0)", + "display": "inline", + "font-style": "italic", + "margin-bottom": "0px", + "margin-left": "0px", + "margin-right": "0px", + "margin-top": "0px" + })raw_string"}; + + for (size_t i = 0; i < computed_styles.size(); i++) { + std::string result_json; + base::JSONWriter::WriteWithOptions(*computed_styles[i], + base::JSONWriter::OPTIONS_PRETTY_PRINT, + &result_json); + + ASSERT_LT(i, expected_styles.size()); + EXPECT_EQ(NormaliseJSON(expected_styles[i]), result_json) << " Style # " + << i; + } + + FinishAsynchronousTest(); + } + + std::unique_ptr<DomTreeExtractor> extractor_; +}; + +HEADLESS_ASYNC_DEVTOOLED_TEST_F(DomTreeExtractorBrowserTest); + +} // namespace headless diff --git a/chromium/headless/public/util/error_reporter.cc b/chromium/headless/public/util/error_reporter.cc index 88722d7b79d..9daa0198670 100644 --- a/chromium/headless/public/util/error_reporter.cc +++ b/chromium/headless/public/util/error_reporter.cc @@ -14,6 +14,7 @@ ErrorReporter::ErrorReporter() {} ErrorReporter::~ErrorReporter() {} +#if DCHECK_IS_ON() void ErrorReporter::Push() { path_.push_back(nullptr); } @@ -47,5 +48,6 @@ void ErrorReporter::AddError(base::StringPiece description) { bool ErrorReporter::HasErrors() const { return !errors_.empty(); } +#endif // DCHECK_IS_ON() } // namespace headless diff --git a/chromium/headless/public/util/error_reporter.h b/chromium/headless/public/util/error_reporter.h index d7eb5e96d21..2fd432f3f2d 100644 --- a/chromium/headless/public/util/error_reporter.h +++ b/chromium/headless/public/util/error_reporter.h @@ -13,12 +13,14 @@ namespace headless { -// Tracks errors which are encountered while parsing client API types. +// Tracks errors which are encountered while parsing client API types. Note that +// errors are only reported in debug builds (i.e., when DCHECK is enabled). class HEADLESS_EXPORT ErrorReporter { public: ErrorReporter(); ~ErrorReporter(); +#if DCHECK_IS_ON() // Enter a new nested parsing context. It will initially have a null name. void Push(); @@ -37,6 +39,14 @@ class HEADLESS_EXPORT ErrorReporter { // Returns a list of reported errors. const std::vector<std::string>& errors() const { return errors_; } +#else // DCHECK_IS_ON() + inline void Push() {} + inline void Pop() {} + inline void SetName(const char* name) {} + inline void AddError(base::StringPiece description) {} + inline bool HasErrors() const { return false; } + std::vector<std::string> errors() const { return std::vector<std::string>(); } +#endif // DCHECK_IS_ON() private: std::vector<const char*> path_; diff --git a/chromium/headless/public/util/error_reporter_unittest.cc b/chromium/headless/public/util/error_reporter_unittest.cc index f23ced9ef29..5752c86b570 100644 --- a/chromium/headless/public/util/error_reporter_unittest.cc +++ b/chromium/headless/public/util/error_reporter_unittest.cc @@ -9,18 +9,22 @@ namespace headless { TEST(ErrorReporterTest, NoErrors) { ErrorReporter reporter; +#if DCHECK_IS_ON() EXPECT_FALSE(reporter.HasErrors()); EXPECT_TRUE(reporter.errors().empty()); +#endif // DCHECK_IS_ON() } TEST(ErrorReporterTest, TopLevelErrors) { ErrorReporter reporter; reporter.AddError("instructions unclear"); reporter.AddError("head stuck in std::unordered_map"); +#if DCHECK_IS_ON() 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]); +#endif // DCHECK_IS_ON() } TEST(ErrorReporterTest, UnnamedContext) { @@ -28,9 +32,11 @@ TEST(ErrorReporterTest, UnnamedContext) { reporter.Push(); reporter.AddError("lp0 is on fire"); reporter.Pop(); +#if DCHECK_IS_ON() EXPECT_TRUE(reporter.HasErrors()); EXPECT_EQ(1u, reporter.errors().size()); EXPECT_EQ("lp0 is on fire", reporter.errors()[0]); +#endif // DCHECK_IS_ON() } TEST(ErrorReporterTest, NestedContexts) { @@ -43,10 +49,12 @@ TEST(ErrorReporterTest, NestedContexts) { reporter.Pop(); reporter.AddError("uh oh"); reporter.Pop(); +#if DCHECK_IS_ON() 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]); +#endif // DCHECK_IS_ON() } } // namespace headless diff --git a/chromium/headless/public/util/generic_url_request_job.cc b/chromium/headless/public/util/generic_url_request_job.cc index 9c7245bc430..a82925d9623 100644 --- a/chromium/headless/public/util/generic_url_request_job.cc +++ b/chromium/headless/public/util/generic_url_request_job.cc @@ -140,6 +140,7 @@ void GenericURLRequestJob::OnFetchComplete( scoped_refptr<net::HttpResponseHeaders> response_headers, const char* body, size_t body_size) { + response_time_ = base::TimeTicks::Now(); http_response_code_ = http_response_code; response_headers_ = response_headers; body_ = body; @@ -186,4 +187,10 @@ bool GenericURLRequestJob::GetCharset(std::string* charset) { return response_headers_->GetCharset(charset); } +void GenericURLRequestJob::GetLoadTimingInfo( + net::LoadTimingInfo* load_timing_info) const { + // TODO(alexclarke): Investigate setting the other members too where possible. + load_timing_info->receive_headers_end = response_time_; +} + } // namespace headless diff --git a/chromium/headless/public/util/generic_url_request_job.h b/chromium/headless/public/util/generic_url_request_job.h index 514a5e176f9..850f0ce6c99 100644 --- a/chromium/headless/public/util/generic_url_request_job.h +++ b/chromium/headless/public/util/generic_url_request_job.h @@ -95,6 +95,7 @@ class GenericURLRequestJob : public ManagedDispatchURLRequestJob, void GetResponseInfo(net::HttpResponseInfo* info) override; bool GetMimeType(std::string* mime_type) const override; bool GetCharset(std::string* charset) override; + void GetLoadTimingInfo(net::LoadTimingInfo* load_timing_info) const override; // URLFetcher::FetchResultListener implementation: void OnFetchStartError(net::Error error) override; @@ -121,6 +122,7 @@ class GenericURLRequestJob : public ManagedDispatchURLRequestJob, int http_response_code_ = 0; size_t body_size_ = 0; size_t read_offset_ = 0; + base::TimeTicks response_time_; base::WeakPtrFactory<GenericURLRequestJob> weak_factory_; diff --git a/chromium/headless/public/util/generic_url_request_job_test.cc b/chromium/headless/public/util/generic_url_request_job_test.cc index 989b5f61638..e1942f86e8b 100644 --- a/chromium/headless/public/util/generic_url_request_job_test.cc +++ b/chromium/headless/public/util/generic_url_request_job_test.cc @@ -86,6 +86,7 @@ class MockFetcher : public URLFetcher { response_headers->AddHeader( base::StringPrintf("%s: %s", it.key().c_str(), value.c_str())); } + result_listener->OnFetchComplete( GURL(final_url), http_response_code, std::move(response_headers), response_data_.c_str(), response_data_.size()); @@ -240,6 +241,10 @@ TEST_F(GenericURLRequestJobTest, BasicRequestContents) { EXPECT_TRUE(request->Read(buffer.get(), kBufferSize, &bytes_read)); EXPECT_EQ(5, bytes_read); EXPECT_EQ("Reply", std::string(buffer->data(), 5)); + + net::LoadTimingInfo load_timing_info; + request->GetLoadTimingInfo(&load_timing_info); + EXPECT_FALSE(load_timing_info.receive_headers_end.is_null()); } TEST_F(GenericURLRequestJobTest, ReadInParts) { diff --git a/chromium/headless/public/util/http_url_fetcher.cc b/chromium/headless/public/util/http_url_fetcher.cc index cbb2c0ad667..af5451218dd 100644 --- a/chromium/headless/public/util/http_url_fetcher.cc +++ b/chromium/headless/public/util/http_url_fetcher.cc @@ -6,6 +6,7 @@ #include "net/base/io_buffer.h" #include "net/cert/cert_status_flags.h" +#include "net/http/http_response_headers.h" #include "net/url_request/url_request.h" #include "net/url_request/url_request_context.h" @@ -165,9 +166,13 @@ void HttpURLFetcher::Delegate::OnResponseCompleted(net::URLRequest* request, return; } - result_listener_->OnFetchCompleteExtractHeaders( - request->url(), request->GetResponseCode(), bytes_read_so_far_.c_str(), - bytes_read_so_far_.size()); + // TODO(alexclarke) apart from the headers there's a lot of stuff in + // |request->response_info()| that we drop here. Find a way to pipe it + // through. + result_listener_->OnFetchComplete( + request->url(), request->GetResponseCode(), + request->response_info().headers, + bytes_read_so_far_.c_str(), bytes_read_so_far_.size()); } HttpURLFetcher::HttpURLFetcher( diff --git a/chromium/headless/public/util/testing/fake_managed_dispatch_url_request_job.cc b/chromium/headless/public/util/testing/fake_managed_dispatch_url_request_job.cc index 24191d3f026..a541cb7af6a 100644 --- a/chromium/headless/public/util/testing/fake_managed_dispatch_url_request_job.cc +++ b/chromium/headless/public/util/testing/fake_managed_dispatch_url_request_job.cc @@ -4,6 +4,8 @@ #include "headless/public/util/testing/fake_managed_dispatch_url_request_job.h" +#include "headless/public/util/url_request_dispatcher.h" + namespace headless { void FakeManagedDispatchURLRequestJob::OnHeadersComplete() { @@ -14,4 +16,8 @@ void FakeManagedDispatchURLRequestJob::OnStartError(net::Error error) { notifications_->push_back(base::StringPrintf("id: %d err: %d", id_, error)); } +void FakeManagedDispatchURLRequestJob::Kill() { + url_request_dispatcher_->JobKilled(this); +} + } // namespace headless diff --git a/chromium/headless/public/util/testing/fake_managed_dispatch_url_request_job.h b/chromium/headless/public/util/testing/fake_managed_dispatch_url_request_job.h index cb0dabdf5b1..79a749bd261 100644 --- a/chromium/headless/public/util/testing/fake_managed_dispatch_url_request_job.h +++ b/chromium/headless/public/util/testing/fake_managed_dispatch_url_request_job.h @@ -31,6 +31,8 @@ class FakeManagedDispatchURLRequestJob : public ManagedDispatchURLRequestJob { using ManagedDispatchURLRequestJob::DispatchHeadersComplete; using ManagedDispatchURLRequestJob::DispatchStartError; + void Kill() override; + void Start() override {} void OnHeadersComplete() override; |