| Index: headless/app/headless_shell.cc
|
| diff --git a/headless/app/headless_shell.cc b/headless/app/headless_shell.cc
|
| index 942fb9bbad7084131d5f94af51c8bdb28cc6e313..91b17493ba08a155c66c66e25fcb1cd9bfd3a85a 100644
|
| --- a/headless/app/headless_shell.cc
|
| +++ b/headless/app/headless_shell.cc
|
| @@ -13,23 +13,13 @@
|
| #include "base/files/file_path.h"
|
| #include "base/json/json_writer.h"
|
| #include "base/location.h"
|
| -#include "base/memory/ptr_util.h"
|
| -#include "base/memory/ref_counted.h"
|
| #include "base/memory/weak_ptr.h"
|
| #include "base/numerics/safe_conversions.h"
|
| #include "base/strings/string_number_conversions.h"
|
| +#include "headless/app/headless_shell.h"
|
| #include "headless/app/headless_shell_switches.h"
|
| -#include "headless/public/devtools/domains/emulation.h"
|
| -#include "headless/public/devtools/domains/inspector.h"
|
| -#include "headless/public/devtools/domains/page.h"
|
| -#include "headless/public/devtools/domains/runtime.h"
|
| -#include "headless/public/headless_browser.h"
|
| -#include "headless/public/headless_devtools_client.h"
|
| #include "headless/public/headless_devtools_target.h"
|
| -#include "headless/public/headless_web_contents.h"
|
| -#include "headless/public/util/deterministic_dispatcher.h"
|
| #include "headless/public/util/deterministic_http_protocol_handler.h"
|
| -#include "net/base/file_stream.h"
|
| #include "net/base/io_buffer.h"
|
| #include "net/base/ip_address.h"
|
| #include "net/base/net_errors.h"
|
| @@ -54,351 +44,351 @@ bool ParseWindowSize(std::string window_size, gfx::Size* parsed_window_size) {
|
| }
|
| } // namespace
|
|
|
| -// An application which implements a simple headless browser.
|
| -class HeadlessShell : public HeadlessWebContents::Observer,
|
| - emulation::ExperimentalObserver,
|
| - inspector::ExperimentalObserver,
|
| - page::Observer {
|
| - public:
|
| - HeadlessShell()
|
| - : browser_(nullptr),
|
| - devtools_client_(HeadlessDevToolsClient::Create()),
|
| - web_contents_(nullptr),
|
| - processed_page_ready_(false),
|
| - browser_context_(nullptr),
|
| - weak_factory_(this) {}
|
| - ~HeadlessShell() override {}
|
| -
|
| - void OnStart(HeadlessBrowser* browser) {
|
| - browser_ = browser;
|
| -
|
| - HeadlessBrowserContext::Builder context_builder =
|
| - browser_->CreateBrowserContextBuilder();
|
| - // TODO(eseckler): These switches should also affect BrowserContexts that
|
| - // are created via DevTools later.
|
| - if (base::CommandLine::ForCurrentProcess()->HasSwitch(
|
| - switches::kDeterministicFetch)) {
|
| - deterministic_dispatcher_.reset(
|
| - new DeterministicDispatcher(browser_->BrowserIOThread()));
|
| -
|
| - ProtocolHandlerMap protocol_handlers;
|
| - protocol_handlers[url::kHttpScheme] =
|
| - base::MakeUnique<DeterministicHttpProtocolHandler>(
|
| - deterministic_dispatcher_.get(), browser->BrowserIOThread());
|
| - protocol_handlers[url::kHttpsScheme] =
|
| - base::MakeUnique<DeterministicHttpProtocolHandler>(
|
| - deterministic_dispatcher_.get(), browser->BrowserIOThread());
|
| -
|
| - context_builder.SetProtocolHandlers(std::move(protocol_handlers));
|
| - }
|
| - browser_context_ = context_builder.Build();
|
| - browser_->SetDefaultBrowserContext(browser_context_);
|
| -
|
| - HeadlessWebContents::Builder builder(
|
| - browser_context_->CreateWebContentsBuilder());
|
| - base::CommandLine::StringVector args =
|
| - base::CommandLine::ForCurrentProcess()->GetArgs();
|
| -
|
| - // TODO(alexclarke): Should we navigate to about:blank first if using
|
| - // virtual time?
|
| - if (args.empty())
|
| - args.push_back("about:blank");
|
| - for (auto it = args.rbegin(); it != args.rend(); ++it) {
|
| - GURL url(*it);
|
| - HeadlessWebContents* web_contents = builder.SetInitialURL(url).Build();
|
| - if (!web_contents) {
|
| - LOG(ERROR) << "Navigation to " << url << " failed";
|
| - browser_->Shutdown();
|
| - return;
|
| - }
|
| - if (!web_contents_ && !RemoteDebuggingEnabled()) {
|
| - // TODO(jzfeng): Support observing multiple targets.
|
| - url_ = url;
|
| - web_contents_ = web_contents;
|
| - web_contents_->AddObserver(this);
|
| - }
|
| - }
|
| - }
|
| -
|
| - void Shutdown() {
|
| - if (!web_contents_)
|
| +HeadlessShell::HeadlessShell()
|
| + : browser_(nullptr),
|
| + devtools_client_(HeadlessDevToolsClient::Create()),
|
| + web_contents_(nullptr),
|
| + processed_page_ready_(false),
|
| + browser_context_(nullptr),
|
| + weak_factory_(this) {}
|
| +
|
| +HeadlessShell::~HeadlessShell() {}
|
| +
|
| +void HeadlessShell::OnStart(HeadlessBrowser* browser) {
|
| + browser_ = browser;
|
| +
|
| + HeadlessBrowserContext::Builder context_builder =
|
| + browser_->CreateBrowserContextBuilder();
|
| + // TODO(eseckler): These switches should also affect BrowserContexts that
|
| + // are created via DevTools later.
|
| + if (base::CommandLine::ForCurrentProcess()->HasSwitch(
|
| + switches::kDeterministicFetch)) {
|
| + deterministic_dispatcher_.reset(
|
| + new DeterministicDispatcher(browser_->BrowserIOThread()));
|
| +
|
| + ProtocolHandlerMap protocol_handlers;
|
| + protocol_handlers[url::kHttpScheme] =
|
| + base::MakeUnique<DeterministicHttpProtocolHandler>(
|
| + deterministic_dispatcher_.get(), browser->BrowserIOThread());
|
| + protocol_handlers[url::kHttpsScheme] =
|
| + base::MakeUnique<DeterministicHttpProtocolHandler>(
|
| + deterministic_dispatcher_.get(), browser->BrowserIOThread());
|
| +
|
| + context_builder.SetProtocolHandlers(std::move(protocol_handlers));
|
| + }
|
| + browser_context_ = context_builder.Build();
|
| + browser_->SetDefaultBrowserContext(browser_context_);
|
| +
|
| + HeadlessWebContents::Builder builder(
|
| + browser_context_->CreateWebContentsBuilder());
|
| + base::CommandLine::StringVector args =
|
| + base::CommandLine::ForCurrentProcess()->GetArgs();
|
| +
|
| + // TODO(alexclarke): Should we navigate to about:blank first if using
|
| + // virtual time?
|
| + if (args.empty())
|
| + args.push_back("about:blank");
|
| + for (auto it = args.rbegin(); it != args.rend(); ++it) {
|
| + GURL url(*it);
|
| + HeadlessWebContents* web_contents = builder.SetInitialURL(url).Build();
|
| + if (!web_contents) {
|
| + LOG(ERROR) << "Navigation to " << url << " failed";
|
| + browser_->Shutdown();
|
| return;
|
| - if (!RemoteDebuggingEnabled()) {
|
| - devtools_client_->GetEmulation()->GetExperimental()->RemoveObserver(this);
|
| - devtools_client_->GetInspector()->GetExperimental()->RemoveObserver(this);
|
| - devtools_client_->GetPage()->RemoveObserver(this);
|
| - if (web_contents_->GetDevToolsTarget()) {
|
| - web_contents_->GetDevToolsTarget()->DetachClient(
|
| - devtools_client_.get());
|
| - }
|
| }
|
| - web_contents_->RemoveObserver(this);
|
| - web_contents_ = nullptr;
|
| - browser_context_->Close();
|
| - browser_->Shutdown();
|
| - }
|
| -
|
| - // HeadlessWebContents::Observer implementation:
|
| - void DevToolsTargetReady() override {
|
| - web_contents_->GetDevToolsTarget()->AttachClient(devtools_client_.get());
|
| - devtools_client_->GetInspector()->GetExperimental()->AddObserver(this);
|
| - devtools_client_->GetPage()->AddObserver(this);
|
| - devtools_client_->GetPage()->Enable();
|
| - // Check if the document had already finished loading by the time we
|
| - // attached.
|
| -
|
| - devtools_client_->GetEmulation()->GetExperimental()->AddObserver(this);
|
| -
|
| - if (base::CommandLine::ForCurrentProcess()->HasSwitch(
|
| - switches::kVirtualTimeBudget)) {
|
| - std::string budget_ms_ascii =
|
| - base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
|
| - switches::kVirtualTimeBudget);
|
| - int budget_ms;
|
| - CHECK(base::StringToInt(budget_ms_ascii, &budget_ms))
|
| - << "Expected an integer value for --virtual-time-budget=";
|
| - devtools_client_->GetEmulation()->GetExperimental()->SetVirtualTimePolicy(
|
| - emulation::SetVirtualTimePolicyParams::Builder()
|
| - .SetPolicy(emulation::VirtualTimePolicy::
|
| - PAUSE_IF_NETWORK_FETCHES_PENDING)
|
| - .SetBudget(budget_ms)
|
| - .Build());
|
| - } else {
|
| - PollReadyState();
|
| - }
|
| -
|
| - if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kTimeout)) {
|
| - std::string timeout_ms_ascii =
|
| - base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
|
| - switches::kTimeout);
|
| - int timeout_ms;
|
| - CHECK(base::StringToInt(timeout_ms_ascii, &timeout_ms))
|
| - << "Expected an integer value for --timeout=";
|
| - browser_->BrowserMainThread()->PostDelayedTask(
|
| - FROM_HERE,
|
| - base::Bind(&HeadlessShell::FetchTimeout, weak_factory_.GetWeakPtr()),
|
| - base::TimeDelta::FromMilliseconds(timeout_ms));
|
| + if (!web_contents_ && !RemoteDebuggingEnabled()) {
|
| + // TODO(jzfeng): Support observing multiple targets.
|
| + url_ = url;
|
| + web_contents_ = web_contents;
|
| + web_contents_->AddObserver(this);
|
| }
|
| -
|
| - // TODO(skyostil): Implement more features to demonstrate the devtools API.
|
| - }
|
| -
|
| - void FetchTimeout() {
|
| - LOG(INFO) << "Timeout.";
|
| - devtools_client_->GetPage()->GetExperimental()->StopLoading(
|
| - page::StopLoadingParams::Builder().Build());
|
| }
|
| +}
|
|
|
| - void OnTargetCrashed(const inspector::TargetCrashedParams& params) override {
|
| - LOG(ERROR) << "Abnormal renderer termination.";
|
| - // NB this never gets called if remote debugging is enabled.
|
| - Shutdown();
|
| +void HeadlessShell::Shutdown() {
|
| + if (!web_contents_)
|
| + return;
|
| + if (!RemoteDebuggingEnabled()) {
|
| + devtools_client_->GetEmulation()->GetExperimental()->RemoveObserver(this);
|
| + devtools_client_->GetInspector()->GetExperimental()->RemoveObserver(this);
|
| + devtools_client_->GetPage()->GetExperimental()->RemoveObserver(this);
|
| + if (web_contents_->GetDevToolsTarget()) {
|
| + web_contents_->GetDevToolsTarget()->DetachClient(devtools_client_.get());
|
| + }
|
| }
|
| + web_contents_->RemoveObserver(this);
|
| + web_contents_ = nullptr;
|
| + browser_context_->Close();
|
| + browser_->Shutdown();
|
| +}
|
|
|
| - void PollReadyState() {
|
| - // We need to check the current location in addition to the ready state to
|
| - // be sure the expected page is ready.
|
| - devtools_client_->GetRuntime()->Evaluate(
|
| - "document.readyState + ' ' + document.location.href",
|
| - base::Bind(&HeadlessShell::OnReadyState, weak_factory_.GetWeakPtr()));
|
| - }
|
| +void HeadlessShell::DevToolsTargetReady() {
|
| + web_contents_->GetDevToolsTarget()->AttachClient(devtools_client_.get());
|
| + devtools_client_->GetInspector()->GetExperimental()->AddObserver(this);
|
| + devtools_client_->GetPage()->GetExperimental()->AddObserver(this);
|
| + devtools_client_->GetPage()->Enable();
|
| + // Check if the document had already finished loading by the time we
|
| + // attached.
|
| +
|
| + devtools_client_->GetEmulation()->GetExperimental()->AddObserver(this);
|
| +
|
| + if (base::CommandLine::ForCurrentProcess()->HasSwitch(
|
| + switches::kDeterministicFetch)) {
|
| + devtools_client_->GetPage()->GetExperimental()->SetControlNavigations(
|
| + headless::page::SetControlNavigationsParams::Builder()
|
| + .SetEnabled(true)
|
| + .Build());
|
| + }
|
| +
|
| + if (base::CommandLine::ForCurrentProcess()->HasSwitch(
|
| + switches::kVirtualTimeBudget)) {
|
| + std::string budget_ms_ascii =
|
| + base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
|
| + switches::kVirtualTimeBudget);
|
| + int budget_ms;
|
| + CHECK(base::StringToInt(budget_ms_ascii, &budget_ms))
|
| + << "Expected an integer value for --virtual-time-budget=";
|
| + devtools_client_->GetEmulation()->GetExperimental()->SetVirtualTimePolicy(
|
| + emulation::SetVirtualTimePolicyParams::Builder()
|
| + .SetPolicy(
|
| + emulation::VirtualTimePolicy::PAUSE_IF_NETWORK_FETCHES_PENDING)
|
| + .SetBudget(budget_ms)
|
| + .Build());
|
| + } else {
|
| + PollReadyState();
|
| + }
|
| +
|
| + if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kTimeout)) {
|
| + std::string timeout_ms_ascii =
|
| + base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
|
| + switches::kTimeout);
|
| + int timeout_ms;
|
| + CHECK(base::StringToInt(timeout_ms_ascii, &timeout_ms))
|
| + << "Expected an integer value for --timeout=";
|
| + browser_->BrowserMainThread()->PostDelayedTask(
|
| + FROM_HERE,
|
| + base::Bind(&HeadlessShell::FetchTimeout, weak_factory_.GetWeakPtr()),
|
| + base::TimeDelta::FromMilliseconds(timeout_ms));
|
| + }
|
| +
|
| + // TODO(skyostil): Implement more features to demonstrate the devtools API.
|
| +}
|
|
|
| - void OnReadyState(std::unique_ptr<runtime::EvaluateResult> result) {
|
| - std::string ready_state_and_url;
|
| - if (result->GetResult()->GetValue()->GetAsString(&ready_state_and_url)) {
|
| - std::stringstream stream(ready_state_and_url);
|
| - std::string ready_state;
|
| - std::string url;
|
| - stream >> ready_state;
|
| - stream >> url;
|
| +void HeadlessShell::FetchTimeout() {
|
| + LOG(INFO) << "Timeout.";
|
| + devtools_client_->GetPage()->GetExperimental()->StopLoading(
|
| + page::StopLoadingParams::Builder().Build());
|
| +}
|
|
|
| - if (ready_state == "complete" &&
|
| - (url_.spec() == url || url != "about:blank")) {
|
| - OnPageReady();
|
| - return;
|
| - }
|
| - }
|
| - }
|
| +void HeadlessShell::OnTargetCrashed(
|
| + const inspector::TargetCrashedParams& params) {
|
| + LOG(ERROR) << "Abnormal renderer termination.";
|
| + // NB this never gets called if remote debugging is enabled.
|
| + Shutdown();
|
| +}
|
|
|
| - // emulation::Observer implementation:
|
| - void OnVirtualTimeBudgetExpired(
|
| - const emulation::VirtualTimeBudgetExpiredParams& params) override {
|
| - OnPageReady();
|
| - }
|
| +void HeadlessShell::PollReadyState() {
|
| + // We need to check the current location in addition to the ready state to
|
| + // be sure the expected page is ready.
|
| + devtools_client_->GetRuntime()->Evaluate(
|
| + "document.readyState + ' ' + document.location.href",
|
| + base::Bind(&HeadlessShell::OnReadyState, weak_factory_.GetWeakPtr()));
|
| +}
|
|
|
| - // page::Observer implementation:
|
| - void OnLoadEventFired(const page::LoadEventFiredParams& params) override {
|
| - if (base::CommandLine::ForCurrentProcess()->HasSwitch(
|
| - switches::kVirtualTimeBudget)) {
|
| +void HeadlessShell::OnReadyState(
|
| + std::unique_ptr<runtime::EvaluateResult> result) {
|
| + std::string ready_state_and_url;
|
| + if (result->GetResult()->GetValue()->GetAsString(&ready_state_and_url)) {
|
| + std::stringstream stream(ready_state_and_url);
|
| + std::string ready_state;
|
| + std::string url;
|
| + stream >> ready_state;
|
| + stream >> url;
|
| +
|
| + if (ready_state == "complete" &&
|
| + (url_.spec() == url || url != "about:blank")) {
|
| + OnPageReady();
|
| return;
|
| }
|
| - OnPageReady();
|
| }
|
| +}
|
|
|
| - void OnPageReady() {
|
| - if (processed_page_ready_)
|
| - return;
|
| - processed_page_ready_ = true;
|
| -
|
| - if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpDom)) {
|
| - FetchDom();
|
| - } else if (base::CommandLine::ForCurrentProcess()->HasSwitch(
|
| - switches::kRepl)) {
|
| - LOG(INFO)
|
| - << "Type a Javascript expression to evaluate or \"quit\" to exit.";
|
| - InputExpression();
|
| - } else if (base::CommandLine::ForCurrentProcess()->HasSwitch(
|
| - switches::kScreenshot)) {
|
| - CaptureScreenshot();
|
| - } else {
|
| - Shutdown();
|
| - }
|
| - }
|
| +// emulation::Observer implementation:
|
| +void HeadlessShell::OnVirtualTimeBudgetExpired(
|
| + const emulation::VirtualTimeBudgetExpiredParams& params) {
|
| + OnPageReady();
|
| +}
|
|
|
| - void FetchDom() {
|
| - devtools_client_->GetRuntime()->Evaluate(
|
| - "document.body.innerHTML",
|
| - base::Bind(&HeadlessShell::OnDomFetched, weak_factory_.GetWeakPtr()));
|
| - }
|
| -
|
| - void OnDomFetched(std::unique_ptr<runtime::EvaluateResult> result) {
|
| - if (result->HasExceptionDetails()) {
|
| - LOG(ERROR) << "Failed to evaluate document.body.innerHTML: "
|
| - << result->GetExceptionDetails()->GetText();
|
| - } else {
|
| - std::string dom;
|
| - if (result->GetResult()->GetValue()->GetAsString(&dom)) {
|
| - printf("%s\n", dom.c_str());
|
| - }
|
| - }
|
| - Shutdown();
|
| +// page::Observer implementation:
|
| +void HeadlessShell::OnLoadEventFired(const page::LoadEventFiredParams& params) {
|
| + if (base::CommandLine::ForCurrentProcess()->HasSwitch(
|
| + switches::kVirtualTimeBudget)) {
|
| + return;
|
| }
|
| + OnPageReady();
|
| +}
|
|
|
| - void InputExpression() {
|
| - // Note that a real system should read user input asynchronously, because
|
| - // otherwise all other browser activity is suspended (e.g., page loading).
|
| - printf(">>> ");
|
| - std::stringstream expression;
|
| - while (true) {
|
| - int c = fgetc(stdin);
|
| - if (c == EOF || c == '\n') {
|
| - break;
|
| - }
|
| - expression << static_cast<char>(c);
|
| - }
|
| - if (expression.str() == "quit") {
|
| - Shutdown();
|
| - return;
|
| - }
|
| - devtools_client_->GetRuntime()->Evaluate(
|
| - expression.str(), base::Bind(&HeadlessShell::OnExpressionResult,
|
| - weak_factory_.GetWeakPtr()));
|
| - }
|
| +void HeadlessShell::OnNavigationRequested(
|
| + const headless::page::NavigationRequestedParams& params) {
|
| + deterministic_dispatcher_->NavigationRequested(
|
| + base::MakeUnique<ShellNavigationRequest>(weak_factory_.GetWeakPtr(),
|
| + params));
|
| +}
|
|
|
| - void OnExpressionResult(std::unique_ptr<runtime::EvaluateResult> result) {
|
| - std::unique_ptr<base::Value> value = result->Serialize();
|
| - std::string result_json;
|
| - base::JSONWriter::Write(*value, &result_json);
|
| - printf("%s\n", result_json.c_str());
|
| +void HeadlessShell::OnPageReady() {
|
| + if (processed_page_ready_)
|
| + return;
|
| + processed_page_ready_ = true;
|
| +
|
| + if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpDom)) {
|
| + FetchDom();
|
| + } else if (base::CommandLine::ForCurrentProcess()->HasSwitch(
|
| + switches::kRepl)) {
|
| + LOG(INFO)
|
| + << "Type a Javascript expression to evaluate or \"quit\" to exit.";
|
| InputExpression();
|
| + } else if (base::CommandLine::ForCurrentProcess()->HasSwitch(
|
| + switches::kScreenshot)) {
|
| + CaptureScreenshot();
|
| + } else {
|
| + Shutdown();
|
| }
|
| +}
|
|
|
| - void CaptureScreenshot() {
|
| - devtools_client_->GetPage()->GetExperimental()->CaptureScreenshot(
|
| - page::CaptureScreenshotParams::Builder().Build(),
|
| - base::Bind(&HeadlessShell::OnScreenshotCaptured,
|
| - weak_factory_.GetWeakPtr()));
|
| - }
|
| +void HeadlessShell::FetchDom() {
|
| + devtools_client_->GetRuntime()->Evaluate(
|
| + "document.body.innerHTML",
|
| + base::Bind(&HeadlessShell::OnDomFetched, weak_factory_.GetWeakPtr()));
|
| +}
|
|
|
| - void OnScreenshotCaptured(
|
| - std::unique_ptr<page::CaptureScreenshotResult> result) {
|
| - base::FilePath file_name =
|
| - base::CommandLine::ForCurrentProcess()->GetSwitchValuePath(
|
| - switches::kScreenshot);
|
| - if (file_name.empty()) {
|
| - file_name = base::FilePath().AppendASCII(kDefaultScreenshotFileName);
|
| +void HeadlessShell::OnDomFetched(
|
| + std::unique_ptr<runtime::EvaluateResult> result) {
|
| + if (result->HasExceptionDetails()) {
|
| + LOG(ERROR) << "Failed to evaluate document.body.innerHTML: "
|
| + << result->GetExceptionDetails()->GetText();
|
| + } else {
|
| + std::string dom;
|
| + if (result->GetResult()->GetValue()->GetAsString(&dom)) {
|
| + printf("%s\n", dom.c_str());
|
| }
|
| + }
|
| + Shutdown();
|
| +}
|
|
|
| - screenshot_file_stream_.reset(
|
| - new net::FileStream(browser_->BrowserFileThread()));
|
| - const int open_result = screenshot_file_stream_->Open(
|
| - file_name, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE |
|
| - base::File::FLAG_ASYNC,
|
| - base::Bind(&HeadlessShell::OnScreenshotFileOpened,
|
| - weak_factory_.GetWeakPtr(), base::Passed(std::move(result)),
|
| - file_name));
|
| - if (open_result != net::ERR_IO_PENDING) {
|
| - // Operation could not be started.
|
| - OnScreenshotFileOpened(nullptr, file_name, open_result);
|
| +void HeadlessShell::InputExpression() {
|
| + // Note that a real system should read user input asynchronously, because
|
| + // otherwise all other browser activity is suspended (e.g., page loading).
|
| + printf(">>> ");
|
| + std::stringstream expression;
|
| + while (true) {
|
| + int c = fgetc(stdin);
|
| + if (c == EOF || c == '\n') {
|
| + break;
|
| }
|
| + expression << static_cast<char>(c);
|
| + }
|
| + if (expression.str() == "quit") {
|
| + Shutdown();
|
| + return;
|
| }
|
| + devtools_client_->GetRuntime()->Evaluate(
|
| + expression.str(), base::Bind(&HeadlessShell::OnExpressionResult,
|
| + weak_factory_.GetWeakPtr()));
|
| +}
|
|
|
| - void OnScreenshotFileOpened(
|
| - std::unique_ptr<page::CaptureScreenshotResult> result,
|
| - const base::FilePath file_name,
|
| - const int open_result) {
|
| - if (open_result != net::OK) {
|
| - LOG(ERROR) << "Writing screenshot to file " << file_name.value()
|
| - << " was unsuccessful, could not open file: "
|
| - << net::ErrorToString(open_result);
|
| - return;
|
| - }
|
| +void HeadlessShell::OnExpressionResult(
|
| + std::unique_ptr<runtime::EvaluateResult> result) {
|
| + std::unique_ptr<base::Value> value = result->Serialize();
|
| + std::string result_json;
|
| + base::JSONWriter::Write(*value, &result_json);
|
| + printf("%s\n", result_json.c_str());
|
| + InputExpression();
|
| +}
|
|
|
| - std::string decoded_png;
|
| - base::Base64Decode(result->GetData(), &decoded_png);
|
| - scoped_refptr<net::IOBufferWithSize> buf =
|
| - new net::IOBufferWithSize(decoded_png.size());
|
| - memcpy(buf->data(), decoded_png.data(), decoded_png.size());
|
| - const int write_result = screenshot_file_stream_->Write(
|
| - buf.get(), buf->size(),
|
| - base::Bind(&HeadlessShell::OnScreenshotFileWritten,
|
| - weak_factory_.GetWeakPtr(), file_name, buf->size()));
|
| - if (write_result != net::ERR_IO_PENDING) {
|
| - // Operation may have completed successfully or failed.
|
| - OnScreenshotFileWritten(file_name, buf->size(), write_result);
|
| - }
|
| - }
|
| +void HeadlessShell::CaptureScreenshot() {
|
| + devtools_client_->GetPage()->GetExperimental()->CaptureScreenshot(
|
| + page::CaptureScreenshotParams::Builder().Build(),
|
| + base::Bind(&HeadlessShell::OnScreenshotCaptured,
|
| + weak_factory_.GetWeakPtr()));
|
| +}
|
|
|
| - void OnScreenshotFileWritten(const base::FilePath file_name,
|
| - const int length,
|
| - const int write_result) {
|
| - if (write_result < length) {
|
| - // TODO(eseckler): Support recovering from partial writes.
|
| - LOG(ERROR) << "Writing screenshot to file " << file_name.value()
|
| - << " was unsuccessful: " << net::ErrorToString(write_result);
|
| - } else {
|
| - LOG(INFO) << "Screenshot written to file " << file_name.value() << "."
|
| - << std::endl;
|
| - }
|
| - int close_result = screenshot_file_stream_->Close(base::Bind(
|
| - &HeadlessShell::OnScreenshotFileClosed, weak_factory_.GetWeakPtr()));
|
| - if (close_result != net::ERR_IO_PENDING) {
|
| - // Operation could not be started.
|
| - OnScreenshotFileClosed(close_result);
|
| - }
|
| +void HeadlessShell::OnScreenshotCaptured(
|
| + std::unique_ptr<page::CaptureScreenshotResult> result) {
|
| + base::FilePath file_name =
|
| + base::CommandLine::ForCurrentProcess()->GetSwitchValuePath(
|
| + switches::kScreenshot);
|
| + if (file_name.empty()) {
|
| + file_name = base::FilePath().AppendASCII(kDefaultScreenshotFileName);
|
| + }
|
| +
|
| + screenshot_file_stream_.reset(
|
| + new net::FileStream(browser_->BrowserFileThread()));
|
| + const int open_result = screenshot_file_stream_->Open(
|
| + file_name, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE |
|
| + base::File::FLAG_ASYNC,
|
| + base::Bind(&HeadlessShell::OnScreenshotFileOpened,
|
| + weak_factory_.GetWeakPtr(), base::Passed(std::move(result)),
|
| + file_name));
|
| + if (open_result != net::ERR_IO_PENDING) {
|
| + // Operation could not be started.
|
| + OnScreenshotFileOpened(nullptr, file_name, open_result);
|
| }
|
| +}
|
|
|
| - void OnScreenshotFileClosed(const int close_result) { Shutdown(); }
|
| +void HeadlessShell::OnScreenshotFileOpened(
|
| + std::unique_ptr<page::CaptureScreenshotResult> result,
|
| + const base::FilePath file_name,
|
| + const int open_result) {
|
| + if (open_result != net::OK) {
|
| + LOG(ERROR) << "Writing screenshot to file " << file_name.value()
|
| + << " was unsuccessful, could not open file: "
|
| + << net::ErrorToString(open_result);
|
| + return;
|
| + }
|
| +
|
| + std::string decoded_png;
|
| + base::Base64Decode(result->GetData(), &decoded_png);
|
| + scoped_refptr<net::IOBufferWithSize> buf =
|
| + new net::IOBufferWithSize(decoded_png.size());
|
| + memcpy(buf->data(), decoded_png.data(), decoded_png.size());
|
| + const int write_result = screenshot_file_stream_->Write(
|
| + buf.get(), buf->size(),
|
| + base::Bind(&HeadlessShell::OnScreenshotFileWritten,
|
| + weak_factory_.GetWeakPtr(), file_name, buf->size()));
|
| + if (write_result != net::ERR_IO_PENDING) {
|
| + // Operation may have completed successfully or failed.
|
| + OnScreenshotFileWritten(file_name, buf->size(), write_result);
|
| + }
|
| +}
|
|
|
| - bool RemoteDebuggingEnabled() const {
|
| - const base::CommandLine& command_line =
|
| - *base::CommandLine::ForCurrentProcess();
|
| - return command_line.HasSwitch(switches::kRemoteDebuggingPort);
|
| +void HeadlessShell::OnScreenshotFileWritten(const base::FilePath file_name,
|
| + const int length,
|
| + const int write_result) {
|
| + if (write_result < length) {
|
| + // TODO(eseckler): Support recovering from partial writes.
|
| + LOG(ERROR) << "Writing screenshot to file " << file_name.value()
|
| + << " was unsuccessful: " << net::ErrorToString(write_result);
|
| + } else {
|
| + LOG(INFO) << "Screenshot written to file " << file_name.value() << "."
|
| + << std::endl;
|
| + }
|
| + int close_result = screenshot_file_stream_->Close(base::Bind(
|
| + &HeadlessShell::OnScreenshotFileClosed, weak_factory_.GetWeakPtr()));
|
| + if (close_result != net::ERR_IO_PENDING) {
|
| + // Operation could not be started.
|
| + OnScreenshotFileClosed(close_result);
|
| }
|
| +}
|
|
|
| - private:
|
| - GURL url_;
|
| - HeadlessBrowser* browser_; // Not owned.
|
| - std::unique_ptr<HeadlessDevToolsClient> devtools_client_;
|
| - HeadlessWebContents* web_contents_;
|
| - bool processed_page_ready_;
|
| - std::unique_ptr<net::FileStream> screenshot_file_stream_;
|
| - HeadlessBrowserContext* browser_context_;
|
| - std::unique_ptr<DeterministicDispatcher> deterministic_dispatcher_;
|
| - base::WeakPtrFactory<HeadlessShell> weak_factory_;
|
| +void HeadlessShell::OnScreenshotFileClosed(const int close_result) {
|
| + Shutdown();
|
| +}
|
|
|
| - DISALLOW_COPY_AND_ASSIGN(HeadlessShell);
|
| -};
|
| +bool HeadlessShell::RemoteDebuggingEnabled() const {
|
| + const base::CommandLine& command_line =
|
| + *base::CommandLine::ForCurrentProcess();
|
| + return command_line.HasSwitch(switches::kRemoteDebuggingPort);
|
| +}
|
|
|
| bool ValidateCommandLine(const base::CommandLine& command_line) {
|
| if (!command_line.HasSwitch(switches::kRemoteDebuggingPort)) {
|
|
|