Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(226)

Unified Diff: headless/app/headless_shell.cc

Issue 2687083002: Headless: make URLRequestDispatcher aware of navigations (Closed)
Patch Set: Address nits Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « headless/app/headless_shell.h ('k') | headless/app/shell_navigation_request.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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)) {
« no previous file with comments | « headless/app/headless_shell.h ('k') | headless/app/shell_navigation_request.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698