Chromium Code Reviews| 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 #if !defined(CHROME_MULTIPLE_DLL_CHILD) | |
|
alex clarke (OOO till 29th)
2017/05/12 11:31:28
It's kind of a shame about all the #ifs although t
dvallet
2017/05/12 21:12:03
That's a good idea Alex!
I'll look into it and wi
| |
| 65 browser_ = browser; | 71 browser_ = browser; |
| 66 | 72 |
| 67 HeadlessBrowserContext::Builder context_builder = | 73 HeadlessBrowserContext::Builder context_builder = |
| 68 browser_->CreateBrowserContextBuilder(); | 74 browser_->CreateBrowserContextBuilder(); |
| 69 // TODO(eseckler): These switches should also affect BrowserContexts that | 75 // TODO(eseckler): These switches should also affect BrowserContexts that |
| 70 // are created via DevTools later. | 76 // are created via DevTools later. |
| 71 DeterministicHttpProtocolHandler* http_handler; | 77 DeterministicHttpProtocolHandler* http_handler = nullptr; |
| 72 DeterministicHttpProtocolHandler* https_handler; | 78 DeterministicHttpProtocolHandler* https_handler = nullptr; |
| 73 if (base::CommandLine::ForCurrentProcess()->HasSwitch( | 79 if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
| 74 switches::kDeterministicFetch)) { | 80 switches::kDeterministicFetch)) { |
| 75 deterministic_dispatcher_.reset( | 81 deterministic_dispatcher_.reset( |
| 76 new DeterministicDispatcher(browser_->BrowserIOThread())); | 82 new DeterministicDispatcher(browser_->BrowserIOThread())); |
| 77 | 83 |
| 78 ProtocolHandlerMap protocol_handlers; | 84 ProtocolHandlerMap protocol_handlers; |
| 79 protocol_handlers[url::kHttpScheme] = | 85 protocol_handlers[url::kHttpScheme] = |
| 80 base::MakeUnique<DeterministicHttpProtocolHandler>( | 86 base::MakeUnique<DeterministicHttpProtocolHandler>( |
| 81 deterministic_dispatcher_.get(), browser->BrowserIOThread()); | 87 deterministic_dispatcher_.get(), browser->BrowserIOThread()); |
| 82 http_handler = static_cast<DeterministicHttpProtocolHandler*>( | 88 http_handler = static_cast<DeterministicHttpProtocolHandler*>( |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 118 browser_->Shutdown(); | 124 browser_->Shutdown(); |
| 119 return; | 125 return; |
| 120 } | 126 } |
| 121 if (!web_contents_ && !RemoteDebuggingEnabled()) { | 127 if (!web_contents_ && !RemoteDebuggingEnabled()) { |
| 122 // TODO(jzfeng): Support observing multiple targets. | 128 // TODO(jzfeng): Support observing multiple targets. |
| 123 url_ = url; | 129 url_ = url; |
| 124 web_contents_ = web_contents; | 130 web_contents_ = web_contents; |
| 125 web_contents_->AddObserver(this); | 131 web_contents_->AddObserver(this); |
| 126 } | 132 } |
| 127 } | 133 } |
| 134 #endif // !defined(CHROME_MULTIPLE_DLL_CHILD) | |
| 128 } | 135 } |
| 129 | 136 |
| 130 void HeadlessShell::Shutdown() { | 137 void HeadlessShell::Shutdown() { |
| 138 #if !defined(CHROME_MULTIPLE_DLL_CHILD) | |
| 131 if (!web_contents_) | 139 if (!web_contents_) |
| 132 return; | 140 return; |
| 133 if (!RemoteDebuggingEnabled()) { | 141 if (!RemoteDebuggingEnabled()) { |
| 134 devtools_client_->GetEmulation()->GetExperimental()->RemoveObserver(this); | 142 devtools_client_->GetEmulation()->GetExperimental()->RemoveObserver(this); |
| 135 devtools_client_->GetInspector()->GetExperimental()->RemoveObserver(this); | 143 devtools_client_->GetInspector()->GetExperimental()->RemoveObserver(this); |
| 136 devtools_client_->GetPage()->GetExperimental()->RemoveObserver(this); | 144 devtools_client_->GetPage()->GetExperimental()->RemoveObserver(this); |
| 137 if (web_contents_->GetDevToolsTarget()) { | 145 if (web_contents_->GetDevToolsTarget()) { |
| 138 web_contents_->GetDevToolsTarget()->DetachClient(devtools_client_.get()); | 146 web_contents_->GetDevToolsTarget()->DetachClient(devtools_client_.get()); |
| 139 } | 147 } |
| 140 } | 148 } |
| 141 web_contents_->RemoveObserver(this); | 149 web_contents_->RemoveObserver(this); |
| 142 web_contents_ = nullptr; | 150 web_contents_ = nullptr; |
| 143 browser_context_->Close(); | 151 browser_context_->Close(); |
| 144 browser_->Shutdown(); | 152 browser_->Shutdown(); |
| 153 #endif // !defined(CHROME_MULTIPLE_DLL_CHILD) | |
| 145 } | 154 } |
| 146 | 155 |
| 147 void HeadlessShell::DevToolsTargetReady() { | 156 void HeadlessShell::DevToolsTargetReady() { |
| 157 #if !defined(CHROME_MULTIPLE_DLL_CHILD) | |
| 148 web_contents_->GetDevToolsTarget()->AttachClient(devtools_client_.get()); | 158 web_contents_->GetDevToolsTarget()->AttachClient(devtools_client_.get()); |
| 149 devtools_client_->GetInspector()->GetExperimental()->AddObserver(this); | 159 devtools_client_->GetInspector()->GetExperimental()->AddObserver(this); |
| 150 devtools_client_->GetPage()->GetExperimental()->AddObserver(this); | 160 devtools_client_->GetPage()->GetExperimental()->AddObserver(this); |
| 151 devtools_client_->GetPage()->Enable(); | 161 devtools_client_->GetPage()->Enable(); |
| 152 | 162 |
| 153 devtools_client_->GetEmulation()->GetExperimental()->AddObserver(this); | 163 devtools_client_->GetEmulation()->GetExperimental()->AddObserver(this); |
| 154 | 164 |
| 155 if (base::CommandLine::ForCurrentProcess()->HasSwitch( | 165 if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
| 156 switches::kDeterministicFetch)) { | 166 switches::kDeterministicFetch)) { |
| 157 devtools_client_->GetPage()->GetExperimental()->SetControlNavigations( | 167 devtools_client_->GetPage()->GetExperimental()->SetControlNavigations( |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 209 int timeout_ms; | 219 int timeout_ms; |
| 210 CHECK(base::StringToInt(timeout_ms_ascii, &timeout_ms)) | 220 CHECK(base::StringToInt(timeout_ms_ascii, &timeout_ms)) |
| 211 << "Expected an integer value for --timeout="; | 221 << "Expected an integer value for --timeout="; |
| 212 browser_->BrowserMainThread()->PostDelayedTask( | 222 browser_->BrowserMainThread()->PostDelayedTask( |
| 213 FROM_HERE, | 223 FROM_HERE, |
| 214 base::Bind(&HeadlessShell::FetchTimeout, weak_factory_.GetWeakPtr()), | 224 base::Bind(&HeadlessShell::FetchTimeout, weak_factory_.GetWeakPtr()), |
| 215 base::TimeDelta::FromMilliseconds(timeout_ms)); | 225 base::TimeDelta::FromMilliseconds(timeout_ms)); |
| 216 } | 226 } |
| 217 | 227 |
| 218 // TODO(skyostil): Implement more features to demonstrate the devtools API. | 228 // TODO(skyostil): Implement more features to demonstrate the devtools API. |
| 229 #endif // !defined(CHROME_MULTIPLE_DLL_CHILD) | |
| 219 } | 230 } |
| 220 | 231 |
| 221 void HeadlessShell::FetchTimeout() { | 232 void HeadlessShell::FetchTimeout() { |
| 222 LOG(INFO) << "Timeout."; | 233 LOG(INFO) << "Timeout."; |
| 223 devtools_client_->GetPage()->GetExperimental()->StopLoading( | 234 devtools_client_->GetPage()->GetExperimental()->StopLoading( |
| 224 page::StopLoadingParams::Builder().Build()); | 235 page::StopLoadingParams::Builder().Build()); |
| 225 } | 236 } |
| 226 | 237 |
| 227 void HeadlessShell::OnTargetCrashed( | 238 void HeadlessShell::OnTargetCrashed( |
| 228 const inspector::TargetCrashedParams& params) { | 239 const inspector::TargetCrashedParams& params) { |
| (...skipping 204 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 433 if (!file_proxy_->Write( | 444 if (!file_proxy_->Write( |
| 434 0, buf->data(), buf->size(), | 445 0, buf->data(), buf->size(), |
| 435 base::Bind(&HeadlessShell::OnFileWritten, weak_factory_.GetWeakPtr(), | 446 base::Bind(&HeadlessShell::OnFileWritten, weak_factory_.GetWeakPtr(), |
| 436 file_name, buf->size()))) { | 447 file_name, buf->size()))) { |
| 437 // Operation may have completed successfully or failed. | 448 // Operation may have completed successfully or failed. |
| 438 OnFileWritten(file_name, buf->size(), base::File::FILE_ERROR_FAILED, 0); | 449 OnFileWritten(file_name, buf->size(), base::File::FILE_ERROR_FAILED, 0); |
| 439 } | 450 } |
| 440 } | 451 } |
| 441 | 452 |
| 442 void HeadlessShell::OnFileWritten(const base::FilePath file_name, | 453 void HeadlessShell::OnFileWritten(const base::FilePath file_name, |
| 443 const int length, | 454 const size_t length, |
| 444 base::File::Error error_code, | 455 base::File::Error error_code, |
| 445 int write_result) { | 456 int write_result) { |
| 446 if (write_result < length) { | 457 if (write_result < static_cast<int>(length)) { |
| 447 // TODO(eseckler): Support recovering from partial writes. | 458 // TODO(eseckler): Support recovering from partial writes. |
| 448 LOG(ERROR) << "Writing to file " << file_name.value() | 459 LOG(ERROR) << "Writing to file " << file_name.value() |
| 449 << " was unsuccessful: " | 460 << " was unsuccessful: " |
| 450 << base::File::ErrorToString(error_code); | 461 << base::File::ErrorToString(error_code); |
| 451 } else { | 462 } else { |
| 452 LOG(INFO) << "Written to file " << file_name.value() << "."; | 463 LOG(INFO) << "Written to file " << file_name.value() << "."; |
| 453 } | 464 } |
| 454 if (!file_proxy_->Close(base::Bind(&HeadlessShell::OnFileClosed, | 465 if (!file_proxy_->Close(base::Bind(&HeadlessShell::OnFileClosed, |
| 455 weak_factory_.GetWeakPtr()))) { | 466 weak_factory_.GetWeakPtr()))) { |
| 456 // Operation could not be started. | 467 // Operation could not be started. |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 520 return false; | 531 return false; |
| 521 } | 532 } |
| 522 if (command_line.HasSwitch(switches::kVirtualTimeBudget)) { | 533 if (command_line.HasSwitch(switches::kVirtualTimeBudget)) { |
| 523 LOG(ERROR) << "Virtual time budget is disabled " | 534 LOG(ERROR) << "Virtual time budget is disabled " |
| 524 << "when remote debugging is enabled."; | 535 << "when remote debugging is enabled."; |
| 525 return false; | 536 return false; |
| 526 } | 537 } |
| 527 return true; | 538 return true; |
| 528 } | 539 } |
| 529 | 540 |
| 541 #if defined(OS_WIN) | |
| 542 int HeadlessShellMain(HINSTANCE instance, | |
| 543 sandbox::SandboxInterfaceInfo* sandbox_info) { | |
| 544 base::CommandLine::Init(0, nullptr); | |
| 545 RunChildProcessIfNeeded(instance, sandbox_info); | |
| 546 HeadlessBrowser::Options::Builder builder(0, nullptr); | |
| 547 builder.SetInstance(instance); | |
| 548 builder.SetSandboxInfo(std::move(sandbox_info)); | |
| 549 #else | |
| 530 int HeadlessShellMain(int argc, const char** argv) { | 550 int HeadlessShellMain(int argc, const char** argv) { |
| 531 base::CommandLine::Init(argc, argv); | 551 base::CommandLine::Init(argc, argv); |
| 532 RunChildProcessIfNeeded(argc, argv); | 552 RunChildProcessIfNeeded(argc, argv); |
| 553 HeadlessBrowser::Options::Builder builder(argc, argv); | |
| 554 #endif // defined(OS_WIN) | |
| 533 HeadlessShell shell; | 555 HeadlessShell shell; |
| 534 HeadlessBrowser::Options::Builder builder(argc, argv); | |
| 535 | 556 |
| 536 const base::CommandLine& command_line( | 557 const base::CommandLine& command_line( |
| 537 *base::CommandLine::ForCurrentProcess()); | 558 *base::CommandLine::ForCurrentProcess()); |
| 538 if (!ValidateCommandLine(command_line)) | 559 if (!ValidateCommandLine(command_line)) |
| 539 return EXIT_FAILURE; | 560 return EXIT_FAILURE; |
| 540 | 561 |
| 541 if (command_line.HasSwitch(::switches::kEnableCrashReporter)) | 562 if (command_line.HasSwitch(switches::kEnableCrashReporter)) |
| 542 builder.SetCrashReporterEnabled(true); | 563 builder.SetCrashReporterEnabled(true); |
| 543 if (command_line.HasSwitch(switches::kCrashDumpsDir)) { | 564 if (command_line.HasSwitch(switches::kCrashDumpsDir)) { |
| 544 builder.SetCrashDumpsDir( | 565 builder.SetCrashDumpsDir( |
| 545 command_line.GetSwitchValuePath(switches::kCrashDumpsDir)); | 566 command_line.GetSwitchValuePath(switches::kCrashDumpsDir)); |
| 546 } | 567 } |
| 547 | 568 |
| 548 // Enable devtools if requested, either by specifying a port (and optional | 569 // Enable devtools if requested, either by specifying a port (and optional |
| 549 // address), or by specifying the fd of an already-open socket. | 570 // address), or by specifying the fd of an already-open socket. |
| 550 if (command_line.HasSwitch(::switches::kRemoteDebuggingPort)) { | 571 if (command_line.HasSwitch(::switches::kRemoteDebuggingPort)) { |
| 551 std::string address = kDevToolsHttpServerAddress; | 572 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); | 653 std::string ua = command_line.GetSwitchValueASCII(switches::kUserAgent); |
| 633 if (net::HttpUtil::IsValidHeaderValue(ua)) | 654 if (net::HttpUtil::IsValidHeaderValue(ua)) |
| 634 builder.SetUserAgent(ua); | 655 builder.SetUserAgent(ua); |
| 635 } | 656 } |
| 636 | 657 |
| 637 return HeadlessBrowserMain( | 658 return HeadlessBrowserMain( |
| 638 builder.Build(), | 659 builder.Build(), |
| 639 base::Bind(&HeadlessShell::OnStart, base::Unretained(&shell))); | 660 base::Bind(&HeadlessShell::OnStart, base::Unretained(&shell))); |
| 640 } | 661 } |
| 641 | 662 |
| 663 int HeadlessShellMain(const content::ContentMainParams& params) { | |
| 664 #if defined(OS_WIN) | |
| 665 return HeadlessShellMain(params.instance, params.sandbox_info); | |
| 666 #else | |
| 667 return HeadlessShellMain(params.argc, params.argv); | |
| 668 #endif | |
| 669 } | |
| 670 | |
| 642 } // namespace headless | 671 } // namespace headless |
| OLD | NEW |