| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include <memory> | 5 #include <memory> |
| 6 #include <sstream> | 6 #include <sstream> |
| 7 #include <string> | 7 #include <string> |
| 8 #include <utility> | 8 #include <utility> |
| 9 | 9 |
| 10 #include "base/base64.h" | 10 #include "base/base64.h" |
| 11 #include "base/base_switches.h" | 11 #include "base/base_switches.h" |
| 12 #include "base/bind.h" | 12 #include "base/bind.h" |
| 13 #include "base/callback.h" | 13 #include "base/callback.h" |
| 14 #include "base/command_line.h" | 14 #include "base/command_line.h" |
| 15 #include "base/files/file_path.h" | 15 #include "base/files/file_path.h" |
| 16 #include "base/json/json_writer.h" | 16 #include "base/json/json_writer.h" |
| 17 #include "base/location.h" | 17 #include "base/location.h" |
| 18 #include "base/memory/weak_ptr.h" | 18 #include "base/memory/weak_ptr.h" |
| 19 #include "base/numerics/safe_conversions.h" | 19 #include "base/numerics/safe_conversions.h" |
| 20 #include "base/strings/string_number_conversions.h" | 20 #include "base/strings/string_number_conversions.h" |
| 21 #include "content/public/app/content_main.h" |
| 21 #include "content/public/common/content_switches.h" | 22 #include "content/public/common/content_switches.h" |
| 22 #include "headless/app/headless_shell.h" | 23 #include "headless/app/headless_shell.h" |
| 23 #include "headless/app/headless_shell_switches.h" | 24 #include "headless/app/headless_shell_switches.h" |
| 24 #include "headless/lib/browser/headless_devtools.h" | 25 #include "headless/lib/browser/headless_devtools.h" |
| 25 #include "headless/public/headless_devtools_target.h" | 26 #include "headless/public/headless_devtools_target.h" |
| 26 #include "headless/public/util/deterministic_http_protocol_handler.h" | 27 #include "headless/public/util/deterministic_http_protocol_handler.h" |
| 27 #include "net/base/io_buffer.h" | 28 #include "net/base/io_buffer.h" |
| 28 #include "net/base/ip_address.h" | 29 #include "net/base/ip_address.h" |
| 29 #include "net/base/net_errors.h" | 30 #include "net/base/net_errors.h" |
| 30 #include "net/http/http_util.h" | 31 #include "net/http/http_util.h" |
| 31 #include "ui/gfx/geometry/size.h" | 32 #include "ui/gfx/geometry/size.h" |
| 32 | 33 |
| 34 #if defined(OS_WIN) |
| 35 #include "sandbox/win/src/sandbox_types.h" |
| 36 #endif |
| 37 |
| 33 namespace headless { | 38 namespace headless { |
| 34 namespace { | 39 namespace { |
| 35 // Address where to listen to incoming DevTools connections. | 40 // Address where to listen to incoming DevTools connections. |
| 36 const char kDevToolsHttpServerAddress[] = "127.0.0.1"; | 41 const char kDevToolsHttpServerAddress[] = "127.0.0.1"; |
| 37 // Default file name for screenshot. Can be overriden by "--screenshot" switch. | 42 // Default file name for screenshot. Can be overriden by "--screenshot" switch. |
| 38 const char kDefaultScreenshotFileName[] = "screenshot.png"; | 43 const char kDefaultScreenshotFileName[] = "screenshot.png"; |
| 39 // Default file name for pdf. Can be overriden by "--print-to-pdf" switch. | 44 // Default file name for pdf. Can be overriden by "--print-to-pdf" switch. |
| 40 const char kDefaultPDFFileName[] = "output.pdf"; | 45 const char kDefaultPDFFileName[] = "output.pdf"; |
| 41 | 46 |
| 42 bool ParseWindowSize(std::string window_size, gfx::Size* parsed_window_size) { | 47 bool ParseWindowSize(std::string window_size, gfx::Size* parsed_window_size) { |
| (...skipping 12 matching lines...) Expand all Loading... |
| 55 : browser_(nullptr), | 60 : browser_(nullptr), |
| 56 devtools_client_(HeadlessDevToolsClient::Create()), | 61 devtools_client_(HeadlessDevToolsClient::Create()), |
| 57 web_contents_(nullptr), | 62 web_contents_(nullptr), |
| 58 processed_page_ready_(false), | 63 processed_page_ready_(false), |
| 59 browser_context_(nullptr), | 64 browser_context_(nullptr), |
| 60 weak_factory_(this) {} | 65 weak_factory_(this) {} |
| 61 | 66 |
| 62 HeadlessShell::~HeadlessShell() {} | 67 HeadlessShell::~HeadlessShell() {} |
| 63 | 68 |
| 64 void HeadlessShell::OnStart(HeadlessBrowser* browser) { | 69 void HeadlessShell::OnStart(HeadlessBrowser* browser) { |
| 70 // TODO(dvallet): Consider making a Windows specific class to make specific |
| 71 // child builds clearer. |
| 72 #if !defined(CHROME_MULTIPLE_DLL_CHILD) |
| 65 browser_ = browser; | 73 browser_ = browser; |
| 66 | 74 |
| 67 HeadlessBrowserContext::Builder context_builder = | 75 HeadlessBrowserContext::Builder context_builder = |
| 68 browser_->CreateBrowserContextBuilder(); | 76 browser_->CreateBrowserContextBuilder(); |
| 69 // TODO(eseckler): These switches should also affect BrowserContexts that | 77 // TODO(eseckler): These switches should also affect BrowserContexts that |
| 70 // are created via DevTools later. | 78 // are created via DevTools later. |
| 71 DeterministicHttpProtocolHandler* http_handler; | 79 DeterministicHttpProtocolHandler* http_handler = nullptr; |
| 72 DeterministicHttpProtocolHandler* https_handler; | 80 DeterministicHttpProtocolHandler* https_handler = nullptr; |
| 73 if (base::CommandLine::ForCurrentProcess()->HasSwitch( | 81 if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
| 74 switches::kDeterministicFetch)) { | 82 switches::kDeterministicFetch)) { |
| 75 deterministic_dispatcher_.reset( | 83 deterministic_dispatcher_.reset( |
| 76 new DeterministicDispatcher(browser_->BrowserIOThread())); | 84 new DeterministicDispatcher(browser_->BrowserIOThread())); |
| 77 | 85 |
| 78 ProtocolHandlerMap protocol_handlers; | 86 ProtocolHandlerMap protocol_handlers; |
| 79 protocol_handlers[url::kHttpScheme] = | 87 protocol_handlers[url::kHttpScheme] = |
| 80 base::MakeUnique<DeterministicHttpProtocolHandler>( | 88 base::MakeUnique<DeterministicHttpProtocolHandler>( |
| 81 deterministic_dispatcher_.get(), browser->BrowserIOThread()); | 89 deterministic_dispatcher_.get(), browser->BrowserIOThread()); |
| 82 http_handler = static_cast<DeterministicHttpProtocolHandler*>( | 90 http_handler = static_cast<DeterministicHttpProtocolHandler*>( |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 118 browser_->Shutdown(); | 126 browser_->Shutdown(); |
| 119 return; | 127 return; |
| 120 } | 128 } |
| 121 if (!web_contents_ && !RemoteDebuggingEnabled()) { | 129 if (!web_contents_ && !RemoteDebuggingEnabled()) { |
| 122 // TODO(jzfeng): Support observing multiple targets. | 130 // TODO(jzfeng): Support observing multiple targets. |
| 123 url_ = url; | 131 url_ = url; |
| 124 web_contents_ = web_contents; | 132 web_contents_ = web_contents; |
| 125 web_contents_->AddObserver(this); | 133 web_contents_->AddObserver(this); |
| 126 } | 134 } |
| 127 } | 135 } |
| 136 #endif // !defined(CHROME_MULTIPLE_DLL_CHILD) |
| 128 } | 137 } |
| 129 | 138 |
| 130 void HeadlessShell::Shutdown() { | 139 void HeadlessShell::Shutdown() { |
| 140 #if !defined(CHROME_MULTIPLE_DLL_CHILD) |
| 131 if (!web_contents_) | 141 if (!web_contents_) |
| 132 return; | 142 return; |
| 133 if (!RemoteDebuggingEnabled()) { | 143 if (!RemoteDebuggingEnabled()) { |
| 134 devtools_client_->GetEmulation()->GetExperimental()->RemoveObserver(this); | 144 devtools_client_->GetEmulation()->GetExperimental()->RemoveObserver(this); |
| 135 devtools_client_->GetInspector()->GetExperimental()->RemoveObserver(this); | 145 devtools_client_->GetInspector()->GetExperimental()->RemoveObserver(this); |
| 136 devtools_client_->GetPage()->GetExperimental()->RemoveObserver(this); | 146 devtools_client_->GetPage()->GetExperimental()->RemoveObserver(this); |
| 137 if (web_contents_->GetDevToolsTarget()) { | 147 if (web_contents_->GetDevToolsTarget()) { |
| 138 web_contents_->GetDevToolsTarget()->DetachClient(devtools_client_.get()); | 148 web_contents_->GetDevToolsTarget()->DetachClient(devtools_client_.get()); |
| 139 } | 149 } |
| 140 } | 150 } |
| 141 web_contents_->RemoveObserver(this); | 151 web_contents_->RemoveObserver(this); |
| 142 web_contents_ = nullptr; | 152 web_contents_ = nullptr; |
| 143 browser_context_->Close(); | 153 browser_context_->Close(); |
| 144 browser_->Shutdown(); | 154 browser_->Shutdown(); |
| 155 #endif // !defined(CHROME_MULTIPLE_DLL_CHILD) |
| 145 } | 156 } |
| 146 | 157 |
| 147 void HeadlessShell::DevToolsTargetReady() { | 158 void HeadlessShell::DevToolsTargetReady() { |
| 159 #if !defined(CHROME_MULTIPLE_DLL_CHILD) |
| 148 web_contents_->GetDevToolsTarget()->AttachClient(devtools_client_.get()); | 160 web_contents_->GetDevToolsTarget()->AttachClient(devtools_client_.get()); |
| 149 devtools_client_->GetInspector()->GetExperimental()->AddObserver(this); | 161 devtools_client_->GetInspector()->GetExperimental()->AddObserver(this); |
| 150 devtools_client_->GetPage()->GetExperimental()->AddObserver(this); | 162 devtools_client_->GetPage()->GetExperimental()->AddObserver(this); |
| 151 devtools_client_->GetPage()->Enable(); | 163 devtools_client_->GetPage()->Enable(); |
| 152 | 164 |
| 153 devtools_client_->GetEmulation()->GetExperimental()->AddObserver(this); | 165 devtools_client_->GetEmulation()->GetExperimental()->AddObserver(this); |
| 154 | 166 |
| 155 if (base::CommandLine::ForCurrentProcess()->HasSwitch( | 167 if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
| 156 switches::kDeterministicFetch)) { | 168 switches::kDeterministicFetch)) { |
| 157 devtools_client_->GetPage()->GetExperimental()->SetControlNavigations( | 169 devtools_client_->GetPage()->GetExperimental()->SetControlNavigations( |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 209 int timeout_ms; | 221 int timeout_ms; |
| 210 CHECK(base::StringToInt(timeout_ms_ascii, &timeout_ms)) | 222 CHECK(base::StringToInt(timeout_ms_ascii, &timeout_ms)) |
| 211 << "Expected an integer value for --timeout="; | 223 << "Expected an integer value for --timeout="; |
| 212 browser_->BrowserMainThread()->PostDelayedTask( | 224 browser_->BrowserMainThread()->PostDelayedTask( |
| 213 FROM_HERE, | 225 FROM_HERE, |
| 214 base::Bind(&HeadlessShell::FetchTimeout, weak_factory_.GetWeakPtr()), | 226 base::Bind(&HeadlessShell::FetchTimeout, weak_factory_.GetWeakPtr()), |
| 215 base::TimeDelta::FromMilliseconds(timeout_ms)); | 227 base::TimeDelta::FromMilliseconds(timeout_ms)); |
| 216 } | 228 } |
| 217 | 229 |
| 218 // TODO(skyostil): Implement more features to demonstrate the devtools API. | 230 // TODO(skyostil): Implement more features to demonstrate the devtools API. |
| 231 #endif // !defined(CHROME_MULTIPLE_DLL_CHILD) |
| 219 } | 232 } |
| 220 | 233 |
| 221 void HeadlessShell::FetchTimeout() { | 234 void HeadlessShell::FetchTimeout() { |
| 222 LOG(INFO) << "Timeout."; | 235 LOG(INFO) << "Timeout."; |
| 223 devtools_client_->GetPage()->GetExperimental()->StopLoading( | 236 devtools_client_->GetPage()->GetExperimental()->StopLoading( |
| 224 page::StopLoadingParams::Builder().Build()); | 237 page::StopLoadingParams::Builder().Build()); |
| 225 } | 238 } |
| 226 | 239 |
| 227 void HeadlessShell::OnTargetCrashed( | 240 void HeadlessShell::OnTargetCrashed( |
| 228 const inspector::TargetCrashedParams& params) { | 241 const inspector::TargetCrashedParams& params) { |
| (...skipping 204 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 433 if (!file_proxy_->Write( | 446 if (!file_proxy_->Write( |
| 434 0, buf->data(), buf->size(), | 447 0, buf->data(), buf->size(), |
| 435 base::Bind(&HeadlessShell::OnFileWritten, weak_factory_.GetWeakPtr(), | 448 base::Bind(&HeadlessShell::OnFileWritten, weak_factory_.GetWeakPtr(), |
| 436 file_name, buf->size()))) { | 449 file_name, buf->size()))) { |
| 437 // Operation may have completed successfully or failed. | 450 // Operation may have completed successfully or failed. |
| 438 OnFileWritten(file_name, buf->size(), base::File::FILE_ERROR_FAILED, 0); | 451 OnFileWritten(file_name, buf->size(), base::File::FILE_ERROR_FAILED, 0); |
| 439 } | 452 } |
| 440 } | 453 } |
| 441 | 454 |
| 442 void HeadlessShell::OnFileWritten(const base::FilePath file_name, | 455 void HeadlessShell::OnFileWritten(const base::FilePath file_name, |
| 443 const int length, | 456 const size_t length, |
| 444 base::File::Error error_code, | 457 base::File::Error error_code, |
| 445 int write_result) { | 458 int write_result) { |
| 446 if (write_result < length) { | 459 if (write_result < static_cast<int>(length)) { |
| 447 // TODO(eseckler): Support recovering from partial writes. | 460 // TODO(eseckler): Support recovering from partial writes. |
| 448 LOG(ERROR) << "Writing to file " << file_name.value() | 461 LOG(ERROR) << "Writing to file " << file_name.value() |
| 449 << " was unsuccessful: " | 462 << " was unsuccessful: " |
| 450 << base::File::ErrorToString(error_code); | 463 << base::File::ErrorToString(error_code); |
| 451 } else { | 464 } else { |
| 452 LOG(INFO) << "Written to file " << file_name.value() << "."; | 465 LOG(INFO) << "Written to file " << file_name.value() << "."; |
| 453 } | 466 } |
| 454 if (!file_proxy_->Close(base::Bind(&HeadlessShell::OnFileClosed, | 467 if (!file_proxy_->Close(base::Bind(&HeadlessShell::OnFileClosed, |
| 455 weak_factory_.GetWeakPtr()))) { | 468 weak_factory_.GetWeakPtr()))) { |
| 456 // Operation could not be started. | 469 // Operation could not be started. |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 520 return false; | 533 return false; |
| 521 } | 534 } |
| 522 if (command_line.HasSwitch(switches::kVirtualTimeBudget)) { | 535 if (command_line.HasSwitch(switches::kVirtualTimeBudget)) { |
| 523 LOG(ERROR) << "Virtual time budget is disabled " | 536 LOG(ERROR) << "Virtual time budget is disabled " |
| 524 << "when remote debugging is enabled."; | 537 << "when remote debugging is enabled."; |
| 525 return false; | 538 return false; |
| 526 } | 539 } |
| 527 return true; | 540 return true; |
| 528 } | 541 } |
| 529 | 542 |
| 543 #if defined(OS_WIN) |
| 544 int HeadlessShellMain(HINSTANCE instance, |
| 545 sandbox::SandboxInterfaceInfo* sandbox_info) { |
| 546 base::CommandLine::Init(0, nullptr); |
| 547 RunChildProcessIfNeeded(instance, sandbox_info); |
| 548 HeadlessBrowser::Options::Builder builder(0, nullptr); |
| 549 builder.SetInstance(instance); |
| 550 builder.SetSandboxInfo(std::move(sandbox_info)); |
| 551 #else |
| 530 int HeadlessShellMain(int argc, const char** argv) { | 552 int HeadlessShellMain(int argc, const char** argv) { |
| 531 base::CommandLine::Init(argc, argv); | 553 base::CommandLine::Init(argc, argv); |
| 532 RunChildProcessIfNeeded(argc, argv); | 554 RunChildProcessIfNeeded(argc, argv); |
| 555 HeadlessBrowser::Options::Builder builder(argc, argv); |
| 556 #endif // defined(OS_WIN) |
| 533 HeadlessShell shell; | 557 HeadlessShell shell; |
| 534 HeadlessBrowser::Options::Builder builder(argc, argv); | |
| 535 | 558 |
| 536 const base::CommandLine& command_line( | 559 const base::CommandLine& command_line( |
| 537 *base::CommandLine::ForCurrentProcess()); | 560 *base::CommandLine::ForCurrentProcess()); |
| 538 if (!ValidateCommandLine(command_line)) | 561 if (!ValidateCommandLine(command_line)) |
| 539 return EXIT_FAILURE; | 562 return EXIT_FAILURE; |
| 540 | 563 |
| 541 if (command_line.HasSwitch(::switches::kEnableCrashReporter)) | 564 if (command_line.HasSwitch(switches::kEnableCrashReporter)) |
| 542 builder.SetCrashReporterEnabled(true); | 565 builder.SetCrashReporterEnabled(true); |
| 543 if (command_line.HasSwitch(switches::kCrashDumpsDir)) { | 566 if (command_line.HasSwitch(switches::kCrashDumpsDir)) { |
| 544 builder.SetCrashDumpsDir( | 567 builder.SetCrashDumpsDir( |
| 545 command_line.GetSwitchValuePath(switches::kCrashDumpsDir)); | 568 command_line.GetSwitchValuePath(switches::kCrashDumpsDir)); |
| 546 } | 569 } |
| 547 | 570 |
| 548 // Enable devtools if requested, either by specifying a port (and optional | 571 // Enable devtools if requested, either by specifying a port (and optional |
| 549 // address), or by specifying the fd of an already-open socket. | 572 // address), or by specifying the fd of an already-open socket. |
| 550 if (command_line.HasSwitch(::switches::kRemoteDebuggingPort)) { | 573 if (command_line.HasSwitch(::switches::kRemoteDebuggingPort)) { |
| 551 std::string address = kDevToolsHttpServerAddress; | 574 std::string address = kDevToolsHttpServerAddress; |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 632 std::string ua = command_line.GetSwitchValueASCII(switches::kUserAgent); | 655 std::string ua = command_line.GetSwitchValueASCII(switches::kUserAgent); |
| 633 if (net::HttpUtil::IsValidHeaderValue(ua)) | 656 if (net::HttpUtil::IsValidHeaderValue(ua)) |
| 634 builder.SetUserAgent(ua); | 657 builder.SetUserAgent(ua); |
| 635 } | 658 } |
| 636 | 659 |
| 637 return HeadlessBrowserMain( | 660 return HeadlessBrowserMain( |
| 638 builder.Build(), | 661 builder.Build(), |
| 639 base::Bind(&HeadlessShell::OnStart, base::Unretained(&shell))); | 662 base::Bind(&HeadlessShell::OnStart, base::Unretained(&shell))); |
| 640 } | 663 } |
| 641 | 664 |
| 665 int HeadlessShellMain(const content::ContentMainParams& params) { |
| 666 #if defined(OS_WIN) |
| 667 return HeadlessShellMain(params.instance, params.sandbox_info); |
| 668 #else |
| 669 return HeadlessShellMain(params.argc, params.argv); |
| 670 #endif |
| 671 } |
| 672 |
| 642 } // namespace headless | 673 } // namespace headless |
| OLD | NEW |